module CryptoID.TH where

import ClassyPrelude

import Language.Haskell.TH

import Data.CryptoID.Class.ImplicitNamespace
import Data.UUID.Types (UUID)
import Data.Binary.SerializationLength

import Data.CaseInsensitive (CI)
import System.FilePath (FilePath)

import Data.Binary (Binary)
import qualified Data.Binary as Binary

import Database.Persist.Sql


decCryptoIDs :: [Name] -> DecsQ
decCryptoIDs = fmap concat . mapM decCryptoID
  where
    decCryptoID :: Name -> DecsQ
    decCryptoID n@(conT -> t) = do
      instances <- [d|
          instance HasFixedSerializationLength $(t) where
            type SerializationLength $(t) = SerializationLength Int64

          instance {-# OVERLAPPING #-} Binary $(t) where
            put = Binary.put . fromSqlKey
            putList = Binary.putList . map fromSqlKey
            get = toSqlKey <$> Binary.get

          type instance CryptoIDNamespace a $(t) = $(litT $ strTyLit ns)
        |]

      synonyms <- mapM cryptoIDSyn
        [ (ConT ''UUID, "UUID")
        , (ConT ''CI `AppT` ConT ''FilePath, "FileName")
        ]

      return $ concat
        [ instances
        , synonyms
        ]
      where
        ns = (\nb -> fromMaybe nb $ stripSuffix "Id" nb) $ nameBase n
        cryptoIDSyn (ct, str) = tySynD (mkName $ "Crypto" ++ str ++ ns) [] $ conT ''CryptoID `appT` return ct `appT` t
