{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE ConstraintKinds #-}

module Database.Record.InternalTH (
  defineTupleInstances
  ) where

import Control.Applicative ((<$>))
import Data.List (foldl')
import Language.Haskell.TH
  (Q, mkName, Name,
   conT, varT, tupleT, appT, classP,
   Dec, instanceD, )

import Database.Record.Persistable (PersistableWidth)
import Database.Record.FromSql (FromSql)
import Database.Record.ToSql (ToSql)


persistableWidth :: Int -> Q [Dec]
persistableWidth n = do
  let vs = [ varT . mkName $ "a" ++ show i | i <- [1 .. n] ]
  (:[]) <$> instanceD
    -- in template-haskell 2.8 or older, Pred is not Type
    (mapM (classP ''PersistableWidth . (:[])) vs)
    [t| PersistableWidth $(foldl' appT (tupleT n) vs) |]
    []

tupleInstance2 :: Int -> Name -> Q [Dec]
tupleInstance2 n clazz = do
  let vs = [ varT . mkName $ "a" ++ show i | i <- [1 .. n] ]
      q = varT $ mkName "q"
  (:[]) <$> instanceD
    -- in template-haskell 2.8 or older, Pred is not Type
    (mapM (\v -> classP clazz [q, v]) vs)
    [t| $(conT clazz) $q $(foldl' appT (tupleT n) vs) |]
    []

-- | Template to define tuple instances of persistable-record classes.
defineTupleInstances :: Int -> Q [Dec]
defineTupleInstances n =
  concat <$> sequence
  [ persistableWidth n
  , tupleInstance2 n ''FromSql
  , tupleInstance2 n ''ToSql ]