{-# LANGUAGE OverloadedStrings, FlexibleInstances, CPP #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}

module JSON (
    Test(..)
  , Case(..)
  , HeaderList
  ) where

#if __GLASGOW_HASKELL__ < 709
import Control.Applicative
#endif
import Control.Monad (mzero)
import Data.Aeson
import Data.ByteString (ByteString)
import qualified Data.ByteString.Char8 as B8
import qualified Data.Aeson.KeyMap as H
import qualified Data.Aeson.Key as Key
import Data.Text (Text)
import qualified Data.Text as T
import Data.Vector ((!))
import qualified Data.Vector as V
import Network.HPACK

{-
import Data.Aeson.Encode.Pretty (encodePretty)
import qualified Data.ByteString.Lazy as BL

main :: IO ()
main = do
    bs <- BL.getContents
    let Right x = eitherDecode bs :: Either String Test
    BL.putStr $ encodePretty x
-}

data Test = Test {
    description :: String
  , cases :: [Case]
  } deriving Show

data Case = Case {
    size :: Maybe Int
  , wire :: ByteString
  , headers :: HeaderList
  , seqno :: Maybe Int
  } deriving Show

instance FromJSON Test where
    parseJSON (Object o) = Test <$> o .: "description"
                                <*> o .: "cases"
    parseJSON _          = mzero

instance ToJSON Test where
    toJSON (Test desc cs) = object ["description" .= desc
                                   ,"cases" .= cs
                                   ]

instance FromJSON Case where
    parseJSON (Object o) = Case <$> o .:? "header_table_size"
                                <*> (textToByteString <$> (o .: "wire"))
                                <*> o .: "headers"
                                <*> o .:? "seqno"
    parseJSON _          = mzero

instance ToJSON Case where
    toJSON (Case (Just siz) w hs no) = object ["header_table_size" .= siz
                                              ,"wire" .= byteStringToText w
                                              ,"headers" .= hs
                                              ,"seqno" .= no
                                              ]
    toJSON (Case Nothing    w hs no) = object ["wire" .= byteStringToText w
                                              ,"headers" .= hs
                                              ,"seqno" .= no
                                              ]

instance {-# OVERLAPPING #-} FromJSON HeaderList where
    parseJSON (Array a) = mapM parseJSON $ V.toList a
    parseJSON _         = mzero

instance {-# OVERLAPPING #-} ToJSON HeaderList where
    toJSON hs = toJSON $ map toJSON hs

instance {-# OVERLAPPING #-} FromJSON Header where
    parseJSON (Array a)  = pure (toKey (a ! 0), toValue (a ! 1)) -- old
      where
        toKey = toValue
    parseJSON (Object o) = pure (textToByteString (Key.toText k), toValue v) -- new
      where
        (k,v) = head $ H.toList o
    parseJSON _          = mzero

instance {-# OVERLAPPING #-} ToJSON Header where
    toJSON (k,v) = object [ Key.fromText (byteStringToText k) .= byteStringToText v ]

textToByteString :: Text -> ByteString
textToByteString = B8.pack . T.unpack

byteStringToText :: ByteString -> Text
byteStringToText = T.pack . B8.unpack

toValue :: Value -> ByteString
toValue (String s) = textToByteString s
toValue _          = error "toValue"
