{-# LANGUAGE OverloadedStrings #-}

module Network.DomainAuth.PRD.Lexer (
    structured
  ) where

import Control.Applicative
import Data.Attoparsec.ByteString (Parser)
import qualified Data.Attoparsec.ByteString as P
import qualified Data.Attoparsec.Combinator as P (choice)
import Data.ByteString (ByteString)
import qualified Data.ByteString as BS
import Data.ByteString.Char8 ()
import Data.Word8

----------------------------------------------------------------

concatSpace :: [ByteString] -> ByteString
concatSpace = BS.intercalate " "

----------------------------------------------------------------

skipChar :: Word8 -> Parser ()
skipChar c = () <$ P.word8 c

skipWsp :: Parser ()
skipWsp = P.skipWhile $ P.inClass " \t\n"

----------------------------------------------------------------

-- |
--
-- >>> P.parseOnly structured "From: Kazu Yamamoto (=?iso-2022-jp?B?GyRCOzNLXE9CSScbKEI=?=)\n <kazu@example.net>"
-- Right ["From",":","Kazu","Yamamoto","<","kazu","@","example",".","net",">"]
-- >>> P.parseOnly structured "To:A Group(Some people)\n      :Chris Jones <c@(Chris's host.)public.example>,\n          joe@example.org,\n   John <jdoe@one.test> (my dear friend); (the end of the group)\n"
-- Right ["To",":","A","Group",":","Chris","Jones","<","c","@","public",".","example",">",",","joe","@","example",".","org",",","John","<","jdoe","@","one",".","test",">",";"]
-- >>> P.parseOnly structured "Date: Thu,\n      13\n        Feb\n          1969\n      23:32\n               -0330 (Newfoundland Time)\n"
-- Right ["Date",":","Thu",",","13","Feb","1969","23",":","32","-0330"]
-- >>> P.parseOnly structured "From: Pete(A nice \\) chap) <pete(his account)@silly.test(his host)>\n"
-- Right ["From",":","Pete","<","pete","@","silly",".","test",">"]
structured :: Parser [ByteString]
structured = removeComments <$> many (P.choice choices)
  where
    removeComments = filter (/="")
    choices = [specials,quotedString,domainLiteral,atom,comment]

specials :: Parser ByteString
specials = specialChar <* skipWsp
  where
    -- removing "()[]\\\""
    specialChar = BS.singleton <$> word8in "<>:;@=,."

----------------------------------------------------------------

atom :: Parser ByteString
atom = atext <* skipWsp
  where
    atext = P.takeWhile1 $ P.inClass "0-9a-zA-Z!#$%&'*+/=?^_`{|}~-"

----------------------------------------------------------------

domainLiteral :: Parser ByteString
domainLiteral = do
    skipChar _bracketleft
    ds <- many (dtext <* skipWsp)
    skipChar _bracketright
    skipWsp
    return $ concatSpace ds
  where
      dtext = P.takeWhile1 $ P.inClass "!-Z^-~"

----------------------------------------------------------------

word8in :: String -> Parser Word8
word8in = P.satisfy . P.inClass

qtext :: Parser Word8
qtext = word8in "!#-[]-~"

qcontent :: Parser Word8
qcontent = qtext <|> quoted_pair

quotedString :: Parser ByteString
quotedString = do
    skipChar _quotedbl
    skipWsp
    qs <- map BS.pack <$> many (some qcontent <* skipWsp)
    skipChar _quotedbl
    skipWsp
    return $ concatSpace qs

----------------------------------------------------------------

quoted_pair :: Parser Word8
quoted_pair = skipChar _backslash >> word8in "!-~ \t\n" -- vchar ++ wsp

----------------------------------------------------------------

ctext :: Parser Word8
ctext = word8in "!-'*-[]-~"

ccontent :: Parser ()
ccontent = () <$ some (ctext <|> quoted_pair)

comment' :: Parser ()
comment' = do
    skipChar _parenleft
    skipWsp
    _ <- many ((ccontent <|> comment') <* skipWsp)
    skipChar _parenright
    skipWsp
    return ()

comment :: Parser ByteString
comment = "" <$ comment'