{-# LANGUAGE FlexibleInstances, TypeSynonymInstances, MultiParamTypeClasses, FunctionalDependencies, NoMonomorphismRestriction, FlexibleContexts #-}
{-# OPTIONS -Wall #-}
module Language.Haskell.TH.Build.Extras where

import Language.Haskell.TH.Build.Convertible
import Language.Haskell.TH.Build.Convertible.Restr
import Language.Haskell.TH.Build.Wrappers
import Language.Haskell.TH
import Control.Monad


getFieldE :: (Convertible a Name) => 
    a       -- ^ Ctor name
    -> Int  -- ^ Ctor arity
    -> Int  -- ^ 0-based index of field to get
    -> Q Exp
getFieldE ctor n i = do
    x <- newName "_x"
    lamE' 
        (conP (name ctor) (map (\j -> if i==j then varP x else wildP) [0..n-1]))     
        x


htuple' :: Convertible a TypeQ => Int -> a -> TypeQ
htuple' n t = foldl appT (tupleT n) (replicate n (typeQ t))

-- * Sugar


-- | = 'lamE''
(\->) :: (Convertible a [PatQ], Convertible a1 ExpQ) =>
                        a -> a1 -> ExpQ
(\->) = lamE' 

infixr 1 \->

class Arrows a b | a -> b, b -> a where
    arrow :: a -> b -> b 

instance Arrows Exp Pat where arrow = ViewP
instance Arrows Type Type where arrow x y = AppT (AppT ArrowT x) y
instance Arrows Kind Kind where arrow = ArrowK

(-->) :: (Convertible qa (Q a), Convertible qb (Q b), Arrows a b) =>
                        qa -> qb -> Q b
(-->) = preconvert2 (liftM2 arrow) 

infixr 1 -->

class Sigs a b c | c -> a b, a -> b c where
    signature :: a -> b -> c

(.::) :: (Convertible qa (Q a'), Convertible qb (Q b'), Sigs a' b' c) => qa -> qb -> Q c
(.::) = preconvert2 (liftM2 signature)

infixl 1 .::

instance Sigs Name Type Dec where signature = SigD
instance Sigs Exp Type Exp where signature = SigE
instance Sigs Pat Type Pat where signature = SigP
instance Sigs Type Kind Type where signature = SigT


-- withLocalNames :: ((String -> Q Name) -> Q b) -> Q b
-- withLocalNames f = do
--     ref <- runIO (newIORef M.empty)
--     let mkLocalName s = do
--         m <- runIO (readIORef ref)
--         case M.lookup s m of
--              Just n -> return n
--              Nothing -> do
--                  n <- newName s
--                  runIO (writeIORef ref (M.insert s n m)) 
--                  return n
-- 
--     f mkLocalName





-- * Variants without usually-empty parameters

-- | Value decl without a @where@-clause
svalD
  :: (Convertible patQ PatQ, Convertible bodyQ BodyQ) =>
     patQ -> bodyQ -> DecQ
svalD p e = valD' p e ()

-- | @case@ match without a @where@-clause
smatch
  :: (Convertible patQ PatQ, Convertible bodyQ BodyQ) =>
     patQ -> bodyQ -> MatchQ
smatch p e = match' p e ()

-- | 'Clause' without a @where@-clause
sclause
  :: (Convertible patQs [PatQ], Convertible bodyQ BodyQ) =>
     patQs -> bodyQ -> ClauseQ
sclause p e = clause' p e ()

-- | @data@ decl with no context
sdataD
  :: (Convertible name Name, Convertible tyVarBndrs [TyVarBndr],
      Convertible conQs [ConQ], Convertible names [Name]) =>
     name -> tyVarBndrs -> conQs -> names -> DecQ
sdataD n vars cons derivs = dataD' () n vars cons derivs

-- | @newtype@ decl with no context
snewtypeD
  :: (Convertible name Name, Convertible tyVarBndrs [TyVarBndr],
      Convertible conQ ConQ, Convertible names [Name]) =>
     name -> tyVarBndrs -> conQ -> names -> DecQ
snewtypeD n vars con derivs = newtypeD' () n vars con derivs