{-# LANGUAGE OverloadedStrings #-}

module Network.DomainAuth.DK.Parser (
    parseDK
  ) where

import Data.ByteString (ByteString)
import qualified Data.ByteString.Char8 as BS
import Data.List
import qualified Data.Map as M
import Data.Maybe
import Network.DomainAuth.DK.Types
import Network.DomainAuth.Mail

-- $setup
-- >>> import Text.Pretty.Simple
-- >>> import Data.ByteString.Char8 as BS8


-- | Parsing DomainKey-Signature:.
--
-- >>> :{
-- let dk = BS8.concat [
--                 "a=rsa-sha1; s=brisbane; d=football.example.com;\n"
--               , "  c=simple; q=dns;\n"
--               , "  b=dzdVyOfAKCdLXdJOc9G2q8LoXSlEniSbav+yuU4zGeeruD00lszZ\n"
--               , "    VoG4ZHRNiYzR;"
--               ]
-- in pPrintNoColor $ parseDK dk
-- :}
-- Just 
--     ( DK 
--         { dkAlgorithm = DK_RSA_SHA1
--         , dkSignature = "dzdVyOfAKCdLXdJOc9G2q8LoXSlEniSbav+yuU4zGeeruD00lszZVoG4ZHRNiYzR"
--         , dkCanonAlgo = DK_SIMPLE
--         , dkDomain0 = "football.example.com"
--         , dkFields = Nothing
--         , dkSelector0 = "brisbane"
--         } 
--     )
parseDK :: RawFieldValue -> Maybe DK
parseDK val = toDK domkey
  where
    (ts,vs) = unzip $ parseTaggedValue val
    fs = map tagToSetter ts
    tagToSetter tag = fromMaybe (\_ mdk -> mdk) $ lookup (BS.head tag) dkTagDB
    pfs = zipWith ($) fs vs
    domkey = foldr ($) initialMDK pfs
    toDK mdk = do
        alg <- mdkAlgorithm mdk
        sig <- mdkSignature mdk
        cal <- mdkCanonAlgo mdk
        dom <- mdkDomain    mdk
        sel <- mdkSelector  mdk
        return DK {
            dkAlgorithm = alg
          , dkSignature = sig
          , dkCanonAlgo = cal
          , dkDomain0   = dom
          , dkFields    = mdkFields mdk
          , dkSelector0 = sel
          }

data MDK = MDK {
    mdkAlgorithm :: Maybe DkAlgorithm
  , mdkSignature :: Maybe ByteString
  , mdkCanonAlgo :: Maybe DkCanonAlgo
  , mdkDomain    :: Maybe ByteString
  , mdkFields    :: Maybe DkFields
  , mdkSelector  :: Maybe ByteString
  } deriving (Eq,Show)

initialMDK :: MDK
initialMDK = MDK {
    mdkAlgorithm = Just DK_RSA_SHA1
  , mdkSignature = Nothing
  , mdkCanonAlgo = Nothing
  , mdkDomain    = Nothing
  , mdkFields    = Nothing
  , mdkSelector  = Nothing
  }

type DKSetter = ByteString -> MDK -> MDK

dkTagDB :: [(Char,DKSetter)]
dkTagDB = [
    ('a',setDkAlgorithm)
  , ('b',setDkSignature)
  , ('c',setDkCanonAlgo)
  , ('d',setDkDomain)
  , ('h',setDkFields)
--  , ('q',setDkQuery)
  , ('s',setDkSelector)
  ]

setDkAlgorithm :: DKSetter
setDkAlgorithm "rsa-sha1" dk = dk { mdkAlgorithm = Just DK_RSA_SHA1 }
setDkAlgorithm _ _           = error "setDkAlgorithm"

setDkSignature :: DKSetter
setDkSignature sig dk = dk { mdkSignature = Just sig }

setDkCanonAlgo :: DKSetter
setDkCanonAlgo "simple" dk = dk { mdkCanonAlgo = Just DK_SIMPLE }
setDkCanonAlgo "nofws"  dk = dk { mdkCanonAlgo = Just DK_NOFWS }
setDkCanonAlgo  _ _        = error "setDkCanonAlgo"

setDkDomain :: DKSetter
setDkDomain dom dk = dk { mdkDomain = Just dom }

setDkFields :: DKSetter
setDkFields keys dk = dk { mdkFields = Just mx }
  where
    flds = BS.split ':' keys
    mx = foldl' func M.empty flds
    func m fld = M.insert fld True m

{-
setDkQuery :: DKSetter
setDkQuery "dns" dk = dk { mdkQuery = Just DK_DNS }
setDkQuery _ _      = error "setDkQuery"
-}

setDkSelector :: DKSetter
setDkSelector sel dk = dk { mdkSelector = Just sel }