{-# LANGUAGE CPP                    #-}
{-# LANGUAGE TemplateHaskell        #-}

-----------------------------------------------------------------------------
-- |
-- Module      :  Generics.EMGM.Derive.Functions
-- Copyright   :  (c) 2008, 2009 Universiteit Utrecht
-- License     :  BSD3
--
-- Maintainer  :  generics@haskell.org
-- Stability   :  experimental
-- Portability :  non-portable
--
-- Summary: Code for generating function-specific instances in TH.
-----------------------------------------------------------------------------

module Generics.EMGM.Derive.Functions (
#ifndef __HADDOCK__
  mkRepCollectInst,
  mkRepEverywhereInst,
  mkRepEverywhereInst',
#endif
) where

#ifndef __HADDOCK__

-----------------------------------------------------------------------------
-- Imports
-----------------------------------------------------------------------------

import Language.Haskell.TH

import Generics.EMGM.Common.Base
import Generics.EMGM.Derive.Common

import Generics.EMGM.Functions.Collect
import Generics.EMGM.Functions.Everywhere

--------------------------------------------------------------------------------

-- | Make the instance for a function-specific Rep instance
mkRepFunctionInst :: DT -> Name -> Q Cxt -> Q Exp -> Q Dec
mkRepFunctionInst dt newtypeName ctx bodyExp = do
  let t = mkAppliedType OptRep dt
  let typ = mkRepInstT OptRep dt (appT (conT newtypeName) t)
  let dec = valD (varP 'rep) (normalB bodyExp) []
  instanceD ctx typ [dec]

--------------------------------------------------------------------------------

-- | Make the instance for a Rep Collect T (where T is the type)
mkRepCollectInst :: DT -> Q Dec
mkRepCollectInst dt = do
  mkRepFunctionInst dt ''Collect (return []) [|Collect (\x -> [x])|]

--------------------------------------------------------------------------------

mkEverywhereFunE :: DT -> Q Exp
mkEverywhereFunE dt = lamE [fpat, xpat] caseExp
  where
    f = mkName "f"
    x = mkName "x"
    xpat = varP x
    fpat = varP f
    appSel = AppE (AppE (AppE (VarE 'selEverywhere) (VarE 'rep)) (VarE f))
    appF = appE (varE f)
    caseExp = caseE (varE x) matches
    matches = zipWith mkMatch pats exps
    mkMatch p e = match (return p) (normalB (appF (return e))) []
    ncs = ncons dt
    pats = map dataP ncs
    exps = map (dataE appSel) ncs

-- | Make the instance for a Rep Everywhere T (where T is the type)
mkRepEverywhereInst :: DT -> Q Dec
mkRepEverywhereInst dt = do
  let dtyp = mkAppliedType OptRep dt
  let typ = appT (conT ''Everywhere) dtyp
  let bodyExp = appE (conE 'Everywhere) (mkEverywhereFunE dt)
  repCtx <- mkRepInstCxt OptRep typ dt
  let ctx = return (tail repCtx)
  mkRepFunctionInst dt ''Everywhere ctx bodyExp

--------------------------------------------------------------------------------

-- | Make the instance for a Rep Everywhere' T (where T is the type)
mkRepEverywhereInst' :: DT -> Q Dec
mkRepEverywhereInst' dt =
  mkRepFunctionInst dt ''Everywhere' (return []) [|Everywhere' (\f x -> f x)|]

#endif