{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeOperators     #-}
{-# LANGUAGE DefaultSignatures #-}
{-# LANGUAGE FlexibleContexts  #-}
-- | Defines Parse class and its generic counterpart
-- for easier parsing of packets.
module Packet.Parse where

import           Data.Char
--import           Data.List           (intercalate)
import           Control.Applicative
import           Debug.Trace
--import           System.IO
-- From bytestring module:
import qualified Data.ByteString.Char8      as BS
-- From Attoparsec
import           Data.Attoparsec.ByteString.Char8 as Atto

-- | Class of things that have a default parsing from ByteString.
class Parse a where
  parser         :: Parser a
  --default parser :: (Generic a, GParse (Rep a)) => Parser a
  --parser          = to <$> gParser

instance Parse Int where
  parser = ord <$> anyChar

instance (Parse a
         ,Parse b) => Parse (a, b) where
  parser = (,) <$> parser
               <*> parser

instance (Parse a
         ,Parse b
         ,Parse c) => Parse (a, b, c) where
  parser = (,,) <$> parser
                <*> parser
                <*> parser

instance (Parse a
         ,Parse b
         ,Parse c
         ,Parse d) => Parse (a, b, c, d) where
  parser = (,,,) <$> parser
                 <*> parser
                 <*> parser
                 <*> parser

-- | Parse ByteString to any value that has Parse instance.
parseBS :: (Parse a) => BS.ByteString -> a
parseBS bs = case parse parser bs of
               Done i r        -> if not $ BS.null i then
                                    trace ("Leftover input: " ++ show  i ++
                                           " of length "      ++ show (BS.length i)) r
                                  else
                                    r
               Partial _       -> error $ "Not enough input to parse anything:\n" ++ show bs
               Fail i ctxs msg -> error $ "ParseError: "  ++ msg ++ "\n" ++ show ctxs ++ "\nat:\n" ++
                                          show i

-- | WARNING: doesn't seem to work!!!
untilEOF :: Parser a -> Parser [a]
untilEOF p = loop []
  where
    loop acc = do
      isEnd <- atEnd
      if isEnd
        then return $ reverse acc
        else do
          n <- p
          loop $ n:acc