module Network.IMAP.Parsers.Fetch where

import           Network.IMAP.Types
import           Network.IMAP.Parsers.Utils
import           Network.IMAP.Parsers.Untagged (parseFlags, parseExtension)

import           Data.Attoparsec.ByteString
import qualified Data.Attoparsec.ByteString as AP
import           Data.Word8
import qualified Data.ByteString.Char8 as BSC
import           Data.Maybe (fromJust, isNothing)

import           Control.Applicative
import           Control.Monad (liftM)


parseFetch :: Parser (Either ErrorMessage CommandResult)
parseFetch = do
  (string "* ") <|> (string "\r\n* ")
  msgId <- liftM toInt $ AP.takeWhile1 isDigit
  let msgId' = msgId >>= Right . MessageId
  string " FETCH ("

  parsedFetch <- parseSpecifiers

  let allInOneEither = sequence $ msgId':parsedFetch
  return $ liftM (Untagged . Fetch) allInOneEither

parseSpecifiers :: Parser [Either ErrorMessage UntaggedResult]
parseSpecifiers = do
  nextChar <- AP.peekWord8
  if isNothing nextChar || (fromJust nextChar == _cr)
    then return []
    else do
      nextRes <- (Right <$> parseEnvelope) <|>
                  parseFlags <|>
                  (Right <$> parseInternalDate) <|>
                  parseNumber Size "RFC822.SIZE" "" <|>
                  ((string "BODY[" <|> string "RFC822.HEADER"
                   <|> string "RFC822.TEXT" <|> string "RFC822") *> parseBody) <|>
                  parseNumber UID "UID" "" <|>
                  (Right <$> parseBodyStructure) <|>
                  parseExtension

      (nextRes:) <$> (AP.anyWord8 *> parseSpecifiers)

parseInternalDate :: Parser UntaggedResult
parseInternalDate = liftM InternalDate $ string "INTERNALDATE " *> parseQuotedText

parseBody :: Parser (Either ErrorMessage UntaggedResult)
parseBody = do
  AP.takeWhile (/= _braceleft)
  word8 _braceleft
  size <- AP.takeWhile1 isDigit
  string "}\r\n"

  case toInt size of
    Right parsedSize -> do
      msg <- AP.take $ fromIntegral parsedSize
      return . Right . Body $ msg
    Left e -> return $ Left e

parseBodyStructure :: Parser UntaggedResult
parseBodyStructure = do
  string "BODYSTRUCTURE " <|> string "BODY "
  structure <- eatUntilClosingParen
  return . BodyStructure $ BSC.snoc structure ')'


parseEnvelope :: Parser UntaggedResult
parseEnvelope = do
  string "ENVELOPE ("
  date <- nilOrValue parseQuotedText
  word8 _space

  subject <- nilOrValue parseQuotedText
  word8 _space

  from <- nilOrValue parseEmailList
  word8 _space

  sender <- nilOrValue parseEmailList
  word8 _space

  replyTo <- nilOrValue parseEmailList
  word8 _space

  to <- nilOrValue parseEmailList
  word8 _space

  cc <- nilOrValue parseEmailList
  word8 _space

  bcc <- nilOrValue parseEmailList
  word8 _space

  inReplyTo <- nilOrValue parseQuotedText
  word8 _space

  messageId <- nilOrValue parseQuotedText
  string ")"

  return Envelope {
    eDate = date,
    eSubject = subject,
    eFrom = from,
    eSender = sender,
    eReplyTo = replyTo,
    eTo = to,
    eCC = cc,
    eBCC = bcc,
    eInReplyTo = inReplyTo,
    eMessageId = messageId
  }