module Mongrel2.Tnetstring
       ( parseTnetstring1
       , TValue(..)
       ) where

import Data.Attoparsec
import Data.Attoparsec.Char8 (decimal)
import Data.ByteString (ByteString)
import Data.Word (Word8)
import Prelude hiding (take)

isColon :: Word8 -> Bool
isColon 0x3a = True
isColon _    = False

data TValue = TInteger Integer
            | TString ByteString
            | TBoolean Bool
            | TList [TValue]
            | TDictionary [(ByteString, TValue)]
            | TNull
              deriving (Show)

subParse :: Parser a -> ByteString -> Parser a
subParse p s = do
  let x = parseOnly p s
  case x of
    Left err -> error err
    Right x' -> return x'

pString :: ByteString -> Parser TValue
pString s = return $ TString s

pInteger :: ByteString -> Parser TValue
pInteger s = fmap TInteger $ subParse decimal s

pBoolean :: ByteString -> Parser TValue
pBoolean "false" = return $ TBoolean False
pBoolean "true"  = return $ TBoolean True
pBoolean _       = error "Invalid boolean literal"

pList :: ByteString -> Parser TValue
pList s = fmap TList $ subParse (many parseTnetstring1) s

pNull :: ByteString -> Parser TValue
pNull _ = return TNull

pDict :: ByteString -> Parser TValue
pDict s =
  fmap TDictionary $ subParse (many parsePair) s
  where
    parsePair :: Parser (ByteString,TValue)
    parsePair = do
      k' <- parseTnetstring1
      k'' <- case k' of
        TString k -> return k
        _         -> error "Dictionary keys may only be strings"
      v <- parseTnetstring1
      return $ (k'', v)

parseTnetstring1 :: Parser TValue
parseTnetstring1 = do
  n <- decimal
  skip isColon
  middle <- take n
  ctyp <- anyWord8
  value <- case ctyp of
    0x2c -> pString middle
    0x23 -> pInteger middle
    0x21 -> pBoolean middle
    0x7e -> pNull middle
    0x5d -> pList middle
    0x7d -> pDict middle
    _    -> error "Invalid type designation"
  return value