{-# LANGUAGE
        TemplateHaskell,
        UndecidableInstances
  #-}

{-# OPTIONS_GHC
        -fno-warn-incomplete-patterns
        -fno-warn-missing-signatures
        -fno-warn-name-shadowing
        -fno-warn-unused-matches
  #-}

{- | This module provides the template to automatically derive a
     Trie implementation from a data type, which uses this data type
     as key. The template generates a trie data type and an instance of the
     'KeyMap' class for every given key, as described in the paper
     \"Efficient, Modular Tries\" by Sebastian Fischer and Frank Huch is
     generated using Template Haskell.

     Usage:

     > import Data.Derive.Trie
     > import Data.KeyMap
     > ...
     > $(deriveTrie [''<keytypename1>,''<keytypename2>,..])

     You will also need to enable the following language extensions:

     - MultiParamTypeClasses
     - TemplateHaskell
     - UndecidableInstances

     And if you want to derive Tries structures for type synonyms you will also
     need:

     - TypeSynonymInstances
 -}
module Data.Derive.Trie
    (
    -- * Deriving Trie structures
      deriveTrie

    -- * Used in generated code
    , tidy
    , ensureTrie
    , trieToMaybe
)  where

import Language.Haskell.TH
import Language.Haskell.TH.Syntax
import Data.Maybe (fromMaybe,isJust,fromJust)
import Control.Monad (foldM)
import Data.List (nub,nubBy,find)
import Debug.Trace
import Data.KeyMap (KeyMap)
import qualified Data.KeyMap as KeyMap
import qualified Data.Map
import qualified Data.IntMap
import Data.Array


-- trie types for some primitive types like Int,Char, ..
prim2trie :: [(Type,Type)]
prim2trie = [(ConT ''Int,    ConT ''Data.IntMap.IntMap),
             (ConT ''Char,   AppT (ConT ''Data.Map.Map) (ConT ''Char)),
             (ConT ''Float,  AppT (ConT ''Data.Map.Map) (ConT ''Float)),
             (ConT ''Double, AppT (ConT ''Data.Map.Map) (ConT ''Double)),
             (ConT ''Array,  AppT (ConT ''Data.Map.Map) (ConT ''Array))
            ]
-- names for the built-in types, whose names would be invalid in user code,
-- e.g. '[]Trie' is a illegal name
nonStandardTrieNamesForKeys :: [(Name,String)]
nonStandardTrieNamesForKeys =
    [(''(),"UnitTrie"),(''[],"ListTrie"),(''(,),"T2Trie"),(''(,,),"T3Trie"),
     (''(,,,),"T4Trie"),(''(,,,,),"T5Trie"),(''(,,,,,),"T6Trie"),
     (''(,,,,,,),"T7Trie")
    ]


-- list of the classes names, which shall occur in the 'deriving' clause
-- empty, because compiling 'deriving' with flag
-- '-fallow-undecidable-instances' traps in ghc-bug (ghc6.6)
classesToDeriveFrom :: [Name]
classesToDeriveFrom = [] --[''Show,''Eq,''Ord]

-- for (Show a, Eq a,..)
standardCxt :: [Name] -> Cxt
standardCxt typevarnames =
  concatMap ((flip mkCxtForClass) typevarnames) classesToDeriveFrom

-- mkCxtForClass ''Eq [a,b] -> (Eq a, Eq b)
mkCxtForClass :: Name -> [Name] -> Cxt
mkCxtForClass _ [] = []
mkCxtForClass classname (t:ts) =
    ClassP classname [VarT t] : mkCxtForClass classname ts

-- main template of this module, for each name given, it builds the data and
-- instance declaration for a trie with the data type noted by the name as key.
-- Further, some helper functions are brought into scope.
-- This template must be called only once within a module, otherwise name
-- clashes are inevitable.
deriveTrie :: [Name] -> Q [Dec]
deriveTrie [] = return []
deriveTrie names = do
  keyDecs <- mapM getDecOfName names
  trieAndInstanceDecs <- mapM deriveTrie' keyDecs
  ns      <- mapM getMaxN keyDecs
  let maxn = maximum ns
  lookupnDecs  <- mapM lookupnD  [0..maxn]
  alternDecs   <- mapM alternD   [0..maxn]
  combinenDecs <- mapM combinenD [0..maxn]
  mapMaybeWithKeynDecs <- mapM mapMaybeWithKeynD [0..maxn]
  toListnDecs  <- mapM toListnD   [0..maxn]
  return (nubBy eqDec (concat trieAndInstanceDecs) ++
          lookupnDecs ++ alternDecs ++ concat combinenDecs ++
          mapMaybeWithKeynDecs ++ toListnDecs)

-- get name's type declaration
getDecOfName :: Name -> Q Dec
getDecOfName name = do
  info <- reify name
  case info of
    TyConI dec -> return dec
    _          -> error "getDecOfName: type constructor expected!"

-- generates the data and instance declaration for a trie with keyDec as key
deriveTrie' :: Dec -> Q [Dec]
deriveTrie' keyDec = do -- Q Monade
       --trace ("derive: "++ nameBase (getNameOfDec keyDec)) (return ())
       (key2trie,trie2keyDecs) <- genTrieDatastructure prim2trie [] keyDec
       let trieDecs = map fst trie2keyDecs
           keyDecs  = map snd trie2keyDecs
           -- sometimes, e.g. when building a Pattern, a dataCon is needed
           -- with type synonyms you may need reify on recently generated types
           -- because this is not possible in TH, the list knownDecs is needed
           -- it is used by getConstrsOfDataDec
           knownDecs = trieDecs ++ keyDecs
           -- log messages to see, what has been generated.
           -- fixxme: does not show used built-in tries
           keyNames = map (nameBase . getNameOfDec . snd) trie2keyDecs
           triePPrints = map (pprint . fst) trie2keyDecs
           loglines = map (\ (t,k) -> k ++ " --> " ++ t)
                          (zip triePPrints keyNames)
       trace (unlines loglines) (return ())
       --trace ("\n" ++ unlines (map (show . fst) trie2keyDecs)) (return ())
       --trace ("\n" ++ unlines (map (show . snd) trie2keyDecs)) (return ())
       instanceDecs <- mapM (uncurry (genKeyMapInstanceDec knownDecs key2trie))
                            trie2keyDecs
       return (trieDecs ++ concat instanceDecs )
               -- ++ lookupnDecs ++ alternDecs ++ (concat combinenDecs))

-- get the maximal arity of all data constructors. is needed to determine max
-- argument of lookupn, altern and combinen.
getMaxN :: Dec -> Q Int
getMaxN dec = getMaxN' [] 0 dec

getMaxN' :: [Name] -> Int -> Dec -> Q Int
getMaxN' visiteds n dec = do
 let name = getNameOfDec dec
 if elem name visiteds
   then return n
   else do cons <- getConstrsOfDataDec [] dec
           let types = map getTypesInCon cons
               maxn  = maximum (n : map length types)
               conTypes =
                   filter isConT (nub (concatMap getBaseTypes (concat types)))
           mconDecs <- mapM (getDecOfType []) conTypes
           let conDecs = map fromJust (filter isJust mconDecs)
           maxns <- mapM (getMaxN' (name : visiteds) maxn) conDecs
           return (maximum (maxn:maxns))

-- get arities of constructors
getNs :: Dec -> Q [Int]
getNs keyDec = do cons <- getConstrsOfDataDec [] keyDec
                  return (map length (map getTypesInCon cons))


-- generates the trie for the given key.
-- key2trie maps keys to already generated tries,
-- knownTrieDecs lists known tries, for lookup in case they need to be reified
genTrieDatastructure :: [(Type,Type)] -> [Dec] -> Dec
                     -> Q ([(Type,Type)],[(Dec,Dec)])
genTrieDatastructure key2trie knownTrieDecs keyDec =
 --trace ("genTrieDatastructure:\n " ++ show key2trie ++ "\n " ++ show knownTrieDecs ++ "\n "++ show keyDec ++ "\n\n") $
 case keyDec of
   TySynD keyName keyTypeVarNames keyType -> do
     if isJust (lookup (ConT keyName) key2trie)
       then return (key2trie,[])
       else do
           let trieBaseName = mkTrieBaseName keyName
           (key2trie',decs,names) <-
               genTrieDataHelper key2trie knownTrieDecs keyName keyTypeVarNames
                                 [[keyType]]
           let knownTrieDecs' = map fst decs ++ knownTrieDecs
           trieDec <- mkTrieNewtypeDec knownTrieDecs' key2trie' trieBaseName
                                       names keyType
           return (key2trie', (trieDec,keyDec) : decs)

   NewtypeD _ keyName keyTypeVarNames con _ -> do
    if isJust (lookup (ConT keyName) key2trie)
      then return (key2trie,[])
      else do
       let [keyType]    = getTypesInCon con
           trieBaseName = mkTrieBaseName keyName
           --trieName = mkName trieBaseName
       (key2trie',decs,names) <-
         genTrieDataHelper key2trie knownTrieDecs keyName keyTypeVarNames
                           [[keyType]]
       let knownTrieDecs' = map fst decs ++ knownTrieDecs
       trieDec <- mkTrieNewtypeDec knownTrieDecs' key2trie' trieBaseName names
                                   keyType
       return (key2trie',(trieDec,keyDec) : decs)

   DataD _ keyName keyTypeVarNames constrs _ ->
    if isJust (lookup (ConT keyName) key2trie)
      then return (key2trie,[])
      else do
       let types    = map getTypesInCon constrs
           trieBaseName = mkTrieBaseName keyName
           trieName = mkName trieBaseName
           knownTrieDecs' = dataDStub trieName : knownTrieDecs
       (key2trie',decs,names) <-
        genTrieDataHelper key2trie knownTrieDecs' keyName keyTypeVarNames types
       let knownTrieDecs'' = map fst decs ++ knownTrieDecs'
       trieDec <- mkTrieDataDec knownTrieDecs'' key2trie' trieBaseName names
                                types
       return (key2trie',(trieDec,keyDec) : decs)
   _ -> error "Can only derive from type, newtype or data declarations!"

-- an empty data declaration
dataDStub :: Name -> Dec
dataDStub name = DataD [] name [] [] []


-- helper for generating the trie datastructure, does the stuff common for
-- data declarations. typesynonyms and newtyps, namely:
-- returns the tries name,typevariables, decs of subtype's tries
genTrieDataHelper :: [(Type,Type)] -> [Dec] -> Name -> [TyVarBndr] -> [[Type]]
                     -> Q ([(Type,Type)],[(Dec,Dec)],(Name,[Name]))
genTrieDataHelper key2trie knownTrieDecs keyName keyTypeVarBndrs constrTypes =
     do
        --trace ("genTrieHelper: " ++ show key2trie ++ "\n" ++ show knownTrieDecs ++"\n" ++nameBase keyName ++ "\n") (return ())
        let keyType = ConT keyName
            trieBaseName = mkTrieBaseName keyName
            trieType = ConT (mkName trieBaseName)
           -- for every type variable of the original type, a type variable for
           -- the corresponding map is needed in trie type declaration (plus a
           -- type variable for values stored in trie)
        valName <- newName "val"
        let keyTypeVarNames = map getNameFromBndr keyTypeVarBndrs
            trieTypeVarNames = map getTrieTypeVar keyTypeVarNames
            var2trie = zip keyTypeVarNames trieTypeVarNames
            key2trie' =
                [(keyType,trieType)] ++
                 key2trie ++ (map (\(x,y) -> (VarT x,VarT y)) var2trie)
            -- generate the tries for the suptypes
            -- baseTypes should only contain VarTs and ConTs
            baseTypes =
               nub (concatMap getBaseTypes (nub (concat constrTypes)))
        mbaseTypeDecs <- mapM (getDecOfType knownTrieDecs)
                              (filter isConT baseTypes)
        let baseTypeDecs = map fromJust (filter isJust mbaseTypeDecs)
        --trace ("genTrieHelper: " ++ show baseTypeDecs++ "\n") (return ())
        (key2trie'',decs) <-
           foldM (\(k2t,ds) d ->do let ktd = knownTrieDecs ++ map fst ds
                                   (k2t',ds') <- genTrieDatastructure k2t ktd d
                                   return (k2t',ds' ++ ds))
                 (key2trie',[])
                 baseTypeDecs
        return (key2trie'',decs,(valName,trieTypeVarNames))

-- using 'show' instead of 'nameBase' is here important,because:
-- show     keyTypeVarName -> a_822037354
-- nameBase keyTypeVarName -> a
-- using mkName instead of newName is also important, because the Decs of
-- built-in types like [] and (,) use identical typevars (a_822083586), which
-- can corrupt the key2trie lists:
--    [..(a_822083586,mapa_1627440534),(a_822083586,mapa_1627440537),..]
getTrieTypeVar :: Name -> Name
getTrieTypeVar keyTypeVarName = mkName ("map" ++ show keyTypeVarName)

-- builds the basename of the trie for the given keyname
-- (usually (keyname ++"Trie))
-- special, non standard key names,like [],(,),.. are replaced by special
-- trienames (ListTrie,T2Trie,..)
mkTrieBaseName :: Name -> String
mkTrieBaseName keyname =
    let nonStandardName = lookup keyname nonStandardTrieNamesForKeys
     in if isJust nonStandardName
         then fromJust nonStandardName
         else nameBase keyname ++ "Trie"

-- builds from several data created before the trie's data declaration
mkTrieDataDec :: [Dec]->[(Type,Type)]-> String -> (Name,[Name]) -> [[Type]]
              -> Q Dec
mkTrieDataDec knownTrieDecs key2trie trieBaseName (valName,trieTypeVarNames)
              constrTypes = do
 conFields <- mapM (mkConField knownTrieDecs valName key2trie) constrTypes
 return (DataD (standardCxt (trieTypeVarNames ++ [valName]))
               (mkName trieBaseName)
               (map PlainTV $ trieTypeVarNames ++ [valName])
               [NormalC (mkName ("No" ++ trieBaseName)) [],
                NormalC (mkName trieBaseName) conFields]
               classesToDeriveFrom)

-- builds from several data created before the trie's type synonym declaration
mkTrieTySynDec :: [Dec] ->[(Type,Type)] -> String -> (Name,[Name]) -> Type
               -> Q Dec
mkTrieTySynDec knownTrieDecs key2trie trieBaseName (valName,trieTypeVarNames)
               keyType = do
    let unAppTrieTypeWithTySyns = replaceKeyByTrie key2trie keyType
    unAppTrieType <-
        replaceTySynTypesByDataType knownTrieDecs unAppTrieTypeWithTySyns
    return (TySynD (mkName trieBaseName)
                   (map PlainTV $ trieTypeVarNames ++ [valName])
                   (AppT unAppTrieType (VarT valName)))

-- builds from several data created before the trie's newtype declaration
-- currently not in use
mkTrieNewtypeDec :: [Dec] ->[(Type,Type)] -> String -> (Name,[Name]) -> Type
             -> Q Dec
mkTrieNewtypeDec knownTrieDecs key2trie trieBaseName (valName,trieTypeVarNames)
                 keyType = do
    let unAppTrieTypeWithTySyns = replaceKeyByTrie key2trie keyType
    unAppTrieType <-
        replaceTySynTypesByDataType knownTrieDecs unAppTrieTypeWithTySyns
    return (NewtypeD []
                     (mkName trieBaseName)
                     (map PlainTV $ trieTypeVarNames ++ [valName])
                     (RecC (mkName trieBaseName)
                           [(mkName ("un"++trieBaseName),
                             NotStrict,
                             AppT unAppTrieType (VarT valName))])
                     [])

-- in the given type, all type synonyms are replaced by the underlying data
-- types
replaceTySynTypesByDataType :: [Dec] -> Type -> Q Type
replaceTySynTypesByDataType knownTrieDecs t = do
  let ot = getOutermostTypeOfType t
      otArgs = getTypeArgs t
  otArgs' <- mapM (replaceTySynTypesByDataType knownTrieDecs) otArgs
  case ot of
   ConT name -> do
     let knownDec = find ((==name).getNameOfDec) knownTrieDecs
     dec <- if isJust knownDec
             then return (fromJust knownDec)
             else do
              info <- reify name
              case info of
               TyConI d -> return d
               i -> error ("replaceTySynTypesByDataType: TyConI expected!\n"++
                           show i)
     case dec of
       TySynD _ tvbndrs (AppT t' val) ->
          let tvlist = map getNameFromBndr tvbndrs
              tv2arg = zip (map VarT tvlist) otArgs'
           in return (replaceArgs tv2arg t')
       TySynD _ _ _ ->
          error "replaceTySynTypesByDataType: invalid trie type synonym!"
       _ -> return (applyTypes ot otArgs')
   v -> do --trace ("in: "++ show t ++ "\nout: " ++ show (applyTypes ot otArgs') ++ "\n") (return ())
           return (applyTypes ot otArgs')


-- if the given type is an application from one type to some args, getTypeArgs
-- returns these args
getTypeArgs :: Type -> [Type]
getTypeArgs (AppT t1 t2) = getTypeArgs t1 ++ [t2]
getTypeArgs _            = []

-- applies type to args
applyTypes :: Type -> [Type] -> Type
applyTypes t [] = t
applyTypes t' (t:ts) = applyTypes (AppT t' t) ts

-- replaces recursively types as specified in the assocList
replaceArgs :: [(Type,Type)] -> Type -> Type
replaceArgs assocList (AppT t1 t2) =
  AppT (replaceArgs assocList t1) (replaceArgs assocList t2)
replaceArgs assocList t =
  let t' = lookup t assocList
  in if isJust t'
      then fromJust t'
      else t

isConT :: Type -> Bool
isConT (ConT _) = True
isConT _        = False

-- if the given type is a type constructor, it's declaration is returned,
-- otherwise Nothing
getDecOfType :: [Dec] -> Type -> Q (Maybe Dec)
getDecOfType knownDecs (ConT name) = do
 let knownDec = find ((==name).getNameOfDec) knownDecs
 if isJust knownDec
   then return knownDec
   else do
     info <- reify name
     case info of
       TyConI tdec -> return (Just tdec)
       _           -> return Nothing
getDecOfType _ _ = return Nothing


-- takes constructor of original datatype and returns corresponding field
-- for trie datatype
mkConField :: [Dec] -> Name -> [(Type,Type)] -> [Type] -> Q StrictType
mkConField knownTrieDecs valname key2trie types = do
 --trace ("mkConField: " ++ show (mkConFieldType valname key2trie types))
  t <- mkConFieldType knownTrieDecs valname key2trie types
  return (IsStrict,t)

mkConFieldType :: [Dec] -> Name -> [(Type,Type)] -> [Type] -> Q Type
mkConFieldType knownTrieDecs valname key2trie types = do
 let replace =
      (replaceTySynTypesByDataType knownTrieDecs) . (replaceKeyByTrie key2trie)
 trieTypesWithoutVal <- mapM replace types
 let trieTypes = addVal trieTypesWithoutVal (VarT valname)
     revTrieTypes = reverse trieTypes
     fieldType = applyTypesAcc (tail revTrieTypes) (head revTrieTypes)
  --trace ("mkConFieldType: " ++ show valname ++ " " ++ show types ++ "\n")
     --replaceTySynTypesByDataType knownTrieDecs fieldType
 return fieldType



isAppT :: Type -> Bool
isAppT (AppT _ _) = True
isAppT _          = False

-- replaces recursively every key type by the corresponding trie type, as
-- specified in key2trie
replaceKeyByTrie :: [(Type,Type)] -> Type -> Type
replaceKeyByTrie key2trie (AppT t1 t2) =
   AppT (replaceKeyByTrie key2trie t1) (replaceKeyByTrie key2trie t2)
replaceKeyByTrie key2trie keyType =
   fromMaybe (AppT (ConT ''Data.Map.Map) keyType) (lookup keyType key2trie)

getTypesInCon :: Con -> [Type]
getTypesInCon (NormalC _ strictTypes) = map snd strictTypes
getTypesInCon (InfixC (_,t1) _ (_,t2)) = [t1,t2]
getTypesInCon (RecC _ varstrictTypes) = map (\(_,_,t) -> t) varstrictTypes
getTypesInCon (ForallC _ _ _) =
  error "Error:getTypesInCon: forallT not supported"


-- returns the undividable types contained in given type. Like removing all
-- AppTs and collecting the single types in a list.
getBaseTypes :: Type -> [Type]
getBaseTypes (ForallT _ _ t) =
    error "Error:getBaseTypes: forallT not supported"
getBaseTypes (AppT t1 t2) = getBaseTypes t1 ++ getBaseTypes t2
getBaseTypes ListT = [ConT ''[]]
getBaseTypes (TupleT _) = [ConT ''(,)]
getBaseTypes ArrowT = error ("Error:getBaseTypes: ArrowT not supported" ++
                             "have you tried to use functions as keys?")
getBaseTypes t = [t]

getNameFromBndr :: TyVarBndr -> Name
getNameFromBndr (PlainTV name) = name
getNameFromBndr (KindedTV name _) = name

-- generates the KeyMap-instance-declaration for given key and trie
genKeyMapInstanceDec :: [Dec] -> [(Type,Type)] -> Dec -> Dec -> Q [Dec]
genKeyMapInstanceDec knownDecs key2trie trieDec keyDec = do
   --trace ("instance: " ++ pprint trieDec) (return ())
 --  let trie2key = map (\ (a,b) -> (b,a)) key2trie
   g_empty   <- gen_empty   knownDecs trieDec
   g_null    <- gen_null    knownDecs trieDec
   g_lookup  <- gen_lookup  knownDecs keyDec trieDec
   g_alter   <- gen_alter   knownDecs keyDec trieDec
   g_combine <- gen_combine knownDecs keyDec trieDec
   g_mapMaybeWithKey <- gen_mapMaybeWithKey knownDecs keyDec trieDec
   g_toList  <- gen_toList  knownDecs keyDec trieDec
   let methods = [ g_empty
                 , g_null
                 , g_lookup
                 , g_alter
                 , g_combine
                 , g_mapMaybeWithKey
                 , g_toList
                 ]

   case trieDec of
     DataD _ triename tvarbndrs _ _ -> do
       let tvarnames = map getNameFromBndr tvarbndrs
       return (mkKeyMapInstanceDec key2trie triename tvarnames methods)
     NewtypeD _ triename tvarbndrs _ _ -> do
       let tvarnames = map getNameFromBndr tvarbndrs
       return (mkKeyMapInstanceDec key2trie triename tvarnames methods)
     _  -> return []
-- builds the instance declaration
mkKeyMapInstanceDec :: [(Type,Type)] -> Name -> [Name] -> [Dec] -> [Dec]
mkKeyMapInstanceDec key2trie triename tvarnames methods =
  let trie2key = map (\ (a,b) -> (b,a)) key2trie
      tvarnamesWithoutVal = take (length tvarnames - 1) tvarnames
      keyMapCxt  = map (mkKeyMapCxt trie2key) tvarnamesWithoutVal
      keyType    = mkKeyType trie2key tvarnamesWithoutVal triename
      trieType   = mkTrieType key2trie keyType
      keyMapType = AppT (AppT (ConT ''KeyMap) keyType) trieType
  in [InstanceD keyMapCxt keyMapType methods]

-- builds the context of the KeyMap-instance-declaration for a given type
-- variable
mkKeyMapCxt :: [(Type,Type)] -> Name -> Pred
mkKeyMapCxt trie2key tvarname =
    let keytvar = fromJust (lookup (VarT tvarname) trie2key)
     in  ClassP ''KeyMap [keytvar, VarT tvarname]

-- builds the key type needed for the KeyMap-instance-declaration
-- variable
mkKeyType :: [(Type,Type)] -> [Name] -> Name -> Type
mkKeyType trie2key tvarnames triename =
    let keytvars = map (fromJust . ((flip lookup) trie2key) . VarT) tvarnames
        keyTypeCon = fromJust (lookup (ConT triename) trie2key)
      in foldl AppT keyTypeCon keytvars

-- builds the trie type needed for the KeyMap-instance-declaration
-- variable
mkTrieType :: [(Type,Type)] -> Type -> Type
mkTrieType key2trie keyType =
    replaceKeyByTrie key2trie keyType

-- generates the empty-method of KeyMap
gen_empty :: [Dec] -> Dec -> Q Dec
gen_empty knownDecs (NewtypeD _ _ _ con _) = do
    let dataconE = conE (getNameOfCon con)
    funD (mkName "empty") [clause [] (normalB [| $dataconE KeyMap.empty|]) []]
gen_empty knownDecs trieDec@(DataD _ _ _ _ _) = do
    noTrieCon <- getNoTrieCon knownDecs trieDec
    funD (mkName "empty")
         [clause [] (normalB (conE (getNameOfCon noTrieCon))) []]


-- generates the null-method of KeyMap
gen_null :: [Dec] -> Dec -> Q Dec
gen_null knownDecs (NewtypeD _ _ _ con _) = do
    (triepat,[varname]) <- mkConPattern con
    let m = varE varname
    funD (mkName "null") [clause [triepat] (normalB [| KeyMap.null ($m)|]) []]
gen_null knownDecs triedec@(DataD _ _ _ _ _) = do
   (emptyTrieConPattern,_) <-
      getNoTrieCon knownDecs triedec >>= mkConPattern
   (nonEmptyTrieConPattern,nonEmptyTrieVarNames) <-
      getNonEmptyTrieCon knownDecs triedec >>= mkConNullPattern knownDecs
   funD (mkName "null")
        [clause [emptyTrieConPattern] (normalB (conE 'True)) [],
         clause [nonEmptyTrieConPattern]
                (nullBody knownDecs triedec nonEmptyTrieVarNames) [],
         clause [wildP] (normalB (conE 'False)) []]

 where mkConNullPattern :: [Dec] -> Con -> Q (PatQ,[Name])
       mkConNullPattern knownDecs con = do
          let types = getTypesInCon con
          patternWithNames <- mapM (getNullPatternForType knownDecs) types
          return (conP (getNameOfCon con) (map fst patternWithNames),
                  concatMap snd patternWithNames)

       getNullPatternForType :: [Dec] ->Type -> Q (PatQ,[Name])
       getNullPatternForType knownDecs t = do
          let ot = getOutermostTypeOfType t
              mdec = find ((== (getNameOfType ot)).getNameOfDec) knownDecs
          if isJust mdec
             then do
               let dec = fromJust mdec
               if isNewtypeD (fromJust mdec)
                then do [con] <- getConstrsOfDataDec knownDecs dec
                        varname <- newName "m"
                        return (conP (getNameOfCon con) [varP varname],
                                [varname])
                else do con <- getNoTrieCon knownDecs (fromJust mdec)
                        return (conP (getNameOfCon con) [],[])
             else do let conName = getNameOfType ot
                     if (conName == ''Maybe)
                      then do  return (conP 'Nothing [],[])
                      else do vname <- newName "x"
                              return (varP vname,[vname])

       nullBody :: [Dec] -> Dec -> [Name] -> BodyQ
       nullBody _ _ [] = normalB (conE 'True)  -- for noTrieCon
       nullBody knownDecs triedec names = do
         con <- getNonEmptyTrieCon knownDecs triedec
         let types = getTypesInCon con
         normalB (appE (varE 'and)
                       (listE (zipWith appE
                                      (map (const (varE 'KeyMap.null)) types)
                                      (map varE names))))



getNameOfType :: Type -> Name
getNameOfType (VarT name) = name
getNameOfType (ConT name) = name
getNameOfType t = error ("Error: getNameOfType: " ++ show t ++ "has no name")

-- returns the data constructors of the given declaration. If the declaration
-- is one of a type synonym, the constructors of the underlying data type are
-- returned
getConstrsOfDataDec :: [Dec] -> Dec -> Q [Con]
getConstrsOfDataDec knownDecs dec =
  case dec of
    DataD _ _ _ cons _ -> return cons
    NewtypeD _ _ _ con _ -> return [con]
    TySynD name _ t    -> do
       let (ConT newname) = getOutermostTypeOfType t
       tdec <- doReify knownDecs newname
       getConstrsOfDataDec knownDecs tdec
    _                 -> error "Error:getConstrsOfDataDec: not implemented!"
 where doReify :: [Dec] -> Name -> Q Dec
       doReify knownDecs name = do
        let knownDec = find ((==name).getNameOfDec) knownDecs
        if isJust knownDec
          then return (fromJust knownDec)
          else do
           info <- reify name
           case info of
             TyConI tdec -> return tdec
             _               -> error "doReify: TyConI expected"

getOutermostTypeOfType :: Type -> Type
getOutermostTypeOfType (AppT t1 t2) = getOutermostTypeOfType t1
getOutermostTypeOfType t = t

-- returns the constructor for the empty trie
-- Assumption: a trie has two cons and the first of them is the one for the
-- empty trie
getNoTrieCon :: [Dec] -> Dec -> Q Con
getNoTrieCon knownDecs triedec = do
  cons <- getConstrsOfDataDec knownDecs triedec
  return (head cons)

-- returns the constructor for the nonempty trie
-- Assumption: a trie has two cons and the first of them is the one for the
-- empty trie
getNonEmptyTrieCon :: [Dec] -> Dec -> Q Con
getNonEmptyTrieCon knownDecs dec = do
  cons <-getConstrsOfDataDec knownDecs dec
  return  (cons !! 1)

-- generates the lookup-method of KeyMap
gen_lookup :: [Dec] -> Dec -> Dec -> Q Dec
gen_lookup knownDecs (NewtypeD _ _ _ keycon _) (NewtypeD _ _ _ triecon _) = do
  (triepat,[trievarname]) <- mkConPattern triecon
  (keypat,[keyvarname])   <- mkConPattern keycon
  let m = varE trievarname
      k = varE keyvarname
  funD (mkName "lookup") [clause [keypat,triepat]
                                 (normalB [| KeyMap.lookup $k $m|])
                                 []]

gen_lookup knownDecs keyDec (NewtypeD _ _ _ triecon _) = do
  (triepat,[trievarname]) <- mkConPattern triecon
  keyvarname <- newName "k"
  let m = varE trievarname
      k = varE keyvarname
  funD (mkName "lookup") [clause [varP keyvarname,triepat]
                                 (normalB [| KeyMap.lookup $k $m|])
                                 []]
gen_lookup knownDecs keyDec trieDec@(DataD _ _ _ _ _) = do
   noTrieCon       <- getNoTrieCon knownDecs trieDec
   nonEmptyTrieCon <- getNonEmptyTrieCon knownDecs trieDec
   keyCons         <- getConstrsOfDataDec knownDecs keyDec

   emptyTrieClause <- gen_lookupClause noTrieCon Nothing
   nonEmptyTrieClauses <-
      mapM (gen_lookupClause nonEmptyTrieCon)
                             (map Just (zip keyCons
                                            [0..]))
   return (FunD (mkName "lookup") (emptyTrieClause : nonEmptyTrieClauses))


-- generates a clause for the lookup-method of KeyMap
-- (con,n) = key's nth data con => recursively call lookup on nth field of
--                                 nonempty triecon
gen_lookupClause :: Con -> Maybe (Con,Int) -> Q Clause
gen_lookupClause trieCon@(NormalC triename _) mkeyCon = do
  case mkeyCon of
    Nothing -> -- triecon is con for empty trie,no key pattern needed
      clause [wildP, conP triename []] (normalB (conE 'Nothing)) []
    Just (keyCon,n) -> do
      (keyPat,keyVarNames)   <- mkConPattern keyCon
      (triePat,trieVarNames) <- mkConPattern trieCon
      let k = length keyVarNames
      clause [keyPat,triePat]
             (normalB (apply (varE (mkName ("lookup" ++ show k)))
                             (map varE keyVarNames ++
                              [varE (trieVarNames !! n)])))
             []
gen_lookupClause tc _ =
    error ("Error:gen_lookupClause: malformed trie constructor: " ++ show tc)


-- returns a pattern for the given con and a list of the names of the variables
-- for the pattern's fields
mkConPattern ::  Con -> Q (PatQ,[Name])
mkConPattern (NormalC name types) = do
  varPNames <- mapM newName (map (const "x") types)
  return (conP name (map varP varPNames),varPNames)
mkConPattern (RecC name types) = do
  varPNames <- mapM newName (map (const "x") types)
  return (conP name (map varP varPNames),varPNames)
mkConPattern (InfixC t1 name t2) = do
  let types = [t1,t2]
  [varPName1,varPName2] <- mapM newName (map (const "x") types)
  return (infixP (varP varPName1) name (varP varPName2),[varPName1,varPName2])
mkConPattern (ForallC _ _ _) =
  error "Error:mkKeyPattern: ForallC not supported!"


 -- generates the alter-method of KeyMap
gen_alter :: [Dec] -> Dec -> Dec -> Q Dec
gen_alter knownDecs (NewtypeD _ _ _ keycon _) (NewtypeD _ _ _ triecon _) = do
  (triepat,[trievarname]) <- mkConPattern triecon
  (keypat,[keyvarname]) <- mkConPattern keycon
  fvarname   <-newName "f"
  let m = varE trievarname
      f = varE fvarname
      k = varE keyvarname
      c = conE (getNameOfCon triecon)
  funD (mkName "alter")
       [clause [keypat,varP fvarname,triepat]
               (normalB [| $c (KeyMap.alter $k $f $m)|])
               []]

gen_alter knownDecs keyDec (NewtypeD _ _ _ con _) = do
  (triepat,[trievarname]) <- mkConPattern con
  fvarname   <-newName "f"
  keyvarname <- newName "k"
  let m = varE trievarname
      f = varE fvarname
      k = varE keyvarname
      c = conE (getNameOfCon con)
  funD (mkName "alter")
       [clause [varP keyvarname,varP fvarname,triepat]
               (normalB [| $c (KeyMap.alter $k $f $m)|])
               []]
gen_alter knownDecs keyDec trieDec@(DataD _ _ _ _ _) = do
   noTrieCon       <- getNoTrieCon knownDecs trieDec
   nonEmptyTrieCon <- getNonEmptyTrieCon knownDecs trieDec
   keyCons         <- getConstrsOfDataDec knownDecs keyDec

   emptyTrieClauses <- mapM (gen_alterClause knownDecs trieDec noTrieCon)
                            (zip keyCons [0..])
   nonEmptyTrieClauses <-
      mapM (gen_alterClause knownDecs trieDec nonEmptyTrieCon)
           (zip keyCons [0..])
   return (FunD (mkName "alter") (emptyTrieClauses ++ nonEmptyTrieClauses))

-- generates a clause for the alter-method of KeyMap
-- (con,n) = key's nth data con
gen_alterClause :: [Dec] -> Dec -> Con -> (Con,Int) -> Q Clause
gen_alterClause knownDecs trieDec trieCon@(NormalC triename _) (keyCon,n) =
 --trace ("genalterclause: " ++ show keyCon ++ "\n" ++ show trieCon) $
 do
  nonEmptyTrieCon <- getNonEmptyTrieCon knownDecs trieDec
  (keyPat,keyVarNames)   <- mkConPattern keyCon
  (triePat,trieVarNames) <- mkConPattern trieCon
  fVarName <- newName "f"

  let emptyTrieFields = mkEmptyTrieFields nonEmptyTrieCon
      oldFields = if trieVarNames == []
                   then map return emptyTrieFields
                   else map varE trieVarNames
      fieldToChange = oldFields !! n
      newField = apply (varE (mkName ("alter" ++ show (length keyVarNames))))
                        (map varE keyVarNames ++ [varE fVarName,fieldToChange])
  clause [keyPat,varP fVarName,triePat]
         (normalB (appE (varE 'tidy)
                        (apply (conE (getConName nonEmptyTrieCon))
                               (take n oldFields ++
                                (newField : drop (n+1) oldFields)))))
         []
gen_alterClause _ _ _ _ =
    error "Error:gen_alterClause: malformed trie constructor!"


getConName :: Con -> Name
getConName (NormalC name _)  = name
getConName (RecC name _)     = name
getConName (InfixC _ name _) = name
getConName _ = error "Error: getConName:Forall not supported"

-- builds default fields for the nonempty triecon
mkEmptyTrieFields :: Con -> [Exp]
mkEmptyTrieFields trieCon =
  map type2empty (getTypesInCon trieCon)
 where type2empty :: Type -> Exp
       type2empty (AppT t _)
           | t == ConT ''Maybe = ConE 'Nothing
           | otherwise = VarE 'KeyMap.empty
       type2empty _                = VarE 'KeyMap.empty


-- generates the combine-method for KeyMap
gen_combine :: [Dec] -> Dec -> Dec -> Q Dec
gen_combine knownDecs keyDec (NewtypeD _ _ _ con _) = do
    (triepat1,[trievarname1]) <- mkConPattern con
    (triepat2,[trievarname2]) <- mkConPattern con
    fname <- newName "f"
    let m1 = varE trievarname1
        m2 = varE trievarname2
        f  = varE fname
        c  = conE (getNameOfCon con)
    funD (mkName "combine")
         [clause [varP fname, triepat1,triepat2]
                 (normalB [| $c (KeyMap.combine $f $m1 $m2)|])
                 []]
gen_combine knownDecs keyDec trieDec@(DataD _ _ _ _ _) = do
   fName <- newName "f"
   ns <- getNs keyDec
   noTrieConName   <- getNoTrieCon knownDecs trieDec >>= (return . getConName)
   nonEmptyTrieCon <- getNonEmptyTrieCon knownDecs trieDec
   let nonEmptyTrieConFields = mkEmptyTrieFields nonEmptyTrieCon

       nonEmptyTrieConName   = getConName nonEmptyTrieCon
       -- emptyTrie = apply (conE nonEmptyTrieConName)
       --                   (map return nonEmptyTrieConFields)
       noTrieConP = conP noTrieConName []
   (nonEmptyTrieConP1,nonEmptyTrieConNames1) <- mkConPattern nonEmptyTrieCon
   (nonEmptyTrieConP2,nonEmptyTrieConNames2) <- mkConPattern nonEmptyTrieCon
  --combinenDecs <- mapM combinenD ns

   emptyEmptyClause <- clause [wildP,noTrieConP,noTrieConP]
                              (normalB (conE noTrieConName))
                              []
   emptyNonEmptyClause <-
       clause [varP fName,noTrieConP,nonEmptyTrieConP2]
              (normalB (appE (varE 'tidy)
                              (apply (conE nonEmptyTrieConName)
                                     (map (combineField (varE fName))
                                          (zip3 ns
                                                (map return
                                                     nonEmptyTrieConFields)
                                                 (map varE
                                                      nonEmptyTrieConNames2)))
                              )))
              []
   nonEmptyEmptyClause <-
      clause [varP fName,nonEmptyTrieConP1,noTrieConP]
             (normalB (appE (varE 'tidy)
                            (apply (conE nonEmptyTrieConName)
                                   (map (combineField (varE fName))
                                        (zip3 ns
                                              (map varE nonEmptyTrieConNames1)
                                              (map return nonEmptyTrieConFields)
                                        )))))
             []
   nonEmptyNonEmptyClause <-
      clause [varP fName,nonEmptyTrieConP1,nonEmptyTrieConP2]
             (normalB (appE (varE 'tidy)
                            (apply (conE nonEmptyTrieConName)
                                   (map (combineField (varE fName))
                                        (zip3 ns
                                              (map varE nonEmptyTrieConNames1)
                                              (map varE nonEmptyTrieConNames2))
                                   ))))
             []

   return (FunD (mkName "combine") [emptyEmptyClause,emptyNonEmptyClause,
                                   nonEmptyEmptyClause,nonEmptyNonEmptyClause])
 where combineField :: ExpQ -> (Int,ExpQ,ExpQ) -> ExpQ
       combineField f (0,a,b) = apply (varE (mkName "combine0")) [f,a,b]
       combineField f (n,a,b) =
          {- appE (varE 'ensureTrie)
                (apply (varE (mkName ("combine"++show n)))
                       [f,appE (varE 'trieToMaybe) a,
                          appE (varE 'trieToMaybe) b])-}
                apply (varE (mkName ("combine"++show n))) [f, a, b]

gen_mapMaybeWithKey :: [Dec] -> Dec -> Dec -> Q Dec
gen_mapMaybeWithKey knownDecs (NewtypeD _ _ _ keycon _)
                              (NewtypeD _ _ _ triecon _) = do
  (triepat,[trievarname]) <- mkConPattern triecon
  fvarname   <-newName "f"
  let m = varE trievarname
      f = varE fvarname
      c = conE (getNameOfCon triecon)
  funD (mkName "mapMaybeWithKey")
       [clause [varP fvarname,triepat]
               (normalB [| $c (KeyMap.mapMaybeWithKey $f $m)|])
               []]

gen_mapMaybeWithKey knownDecs keyDec (NewtypeD _ _ _ con _) = do
  (triepat,[trievarname]) <- mkConPattern con
  fvarname   <-newName "f"
  let m = varE trievarname
      f = varE fvarname
      c = conE (getNameOfCon con)
  funD (mkName "mapMaybeWithKey")
       [clause [varP fvarname,triepat]
               (normalB [| $c (KeyMap.mapMaybeWithKey $f $m)|])
               []]
gen_mapMaybeWithKey knownDecs keyDec trieDec@(DataD _ _ _ _ _) = do
   noTrieCon       <- getNoTrieCon knownDecs trieDec
   let noTrieName = getNameOfCon noTrieCon
   nonEmptyTrieCon <- getNonEmptyTrieCon knownDecs trieDec
   fvarname   <-newName "f"

   emptyTrieClause <- clause [varP fvarname, conP noTrieName []]
                             (normalB $ conE noTrieName) []
   nonEmptyTrieClause <-
      gen_mapMaybeWithKeyClause knownDecs keyDec trieDec nonEmptyTrieCon
   return (FunD (mkName "mapMaybeWithKey")
                [emptyTrieClause, nonEmptyTrieClause])

gen_mapMaybeWithKeyClause :: [Dec] -> Dec -> Dec -> Con -> Q Clause
gen_mapMaybeWithKeyClause knownDecs keyDec trieDec trieCon@(NormalC triename _) =
 do
  nonEmptyTrieCon <- getNonEmptyTrieCon knownDecs trieDec
  (triePat,trieVarNames) <- mkConPattern trieCon
  fVarName <- newName "f"
  ns <- getNs keyDec
  cons <- getConstrsOfDataDec knownDecs keyDec

  let oldFields = map varE trieVarNames
      newFields = zipWith3 (\ n con o -> apply (varE 
                                                (mkName $
                                                 "mapMaybeWithKey" ++ show n))
                                               [conE $ getNameOfCon con, o,
                                                    varE fVarName])
                           ns cons oldFields
  clause [varP fVarName,triePat]
         (normalB (appE (varE 'tidy)
                        (apply (conE (getConName nonEmptyTrieCon)) newFields)))
         []
gen_mapMaybeWithKeyClause _ _ _ _ =
    error "Error:gen_mapMaybeWithKeyClause: malformed trie constructor!"

{-
nth :: String -> Int -> [a] -> a
nth descr n list = --trace ("nth " ++ show n ++ " " ++ descr ++ "\n")
                   (nth' descr n list)

nth' :: String -> Int -> [a] -> a
nth' d _ [] = error (d ++ ": nth: Index to large!")
nth' _ 0 (x:_) = x
nth' d n (_:xs) = nth' d (n-1) xs

fromJust' :: String -> Maybe a -> a
fromJust' d Nothing = error d
fromJust' _ (Just a) = a

reify' :: String -> Name -> Q Info
reify' str name = do
  --trace (str++ " " ++ nameBase name) (return ())
  reify name

-}
getNameOfDec :: Dec -> Name
getNameOfDec (FunD name _)           = name
getNameOfDec (DataD _ name _ _ _)    = name
getNameOfDec (NewtypeD _ name _ _ _) = name
getNameOfDec (TySynD name _ _)       = name
getNameOfDec (ClassD _ name _ _ _)   = name
getNameOfDec (SigD name _)           = name
getNameOfDec dec = error ("getNameOfDec: " ++ show dec ++ " has no name!")

-- equality test for decs, (==) is not suitable,because the same decs with
-- only different named variables are supposed to be different
eqDec :: Dec -> Dec -> Bool
eqDec (InstanceD _ t1 _) (InstanceD _ t2 _) = eqTypeIgnoreVarNames t1 t2
eqDec (InstanceD _ t1 _) d2 = False
eqDec d1 (InstanceD _ t1 _) = False
eqDec d1 d2 = getNameOfDec d1 == getNameOfDec d2

-- equality test for Types, (==) is not suitable,because the same Types with
-- only different named variables are supposed to be different
eqTypeIgnoreVarNames :: Type -> Type -> Bool
eqTypeIgnoreVarNames (AppT t1 t2) (AppT t1' t2') =
    eqTypeIgnoreVarNames t1 t1' && eqTypeIgnoreVarNames t2 t2'
eqTypeIgnoreVarNames (ForallT names cxt t) (ForallT names' cxt' t') =
  eqTypeIgnoreVarNames t t'  -- for use here, this is sufficient
eqTypeIgnoreVarNames (VarT _) (VarT _) = True
eqTypeIgnoreVarNames t1 t2 = t1 == t2

getNameOfCon :: Con -> Name
getNameOfCon (NormalC name _)  = name
getNameOfCon (RecC name _)     = name
getNameOfCon (InfixC _ name _) = name
getNameOfCon (ForallC _ _ con) = getNameOfCon con

-- makes expression, where f is applied to args
apply :: ExpQ -> [ExpQ] -> ExpQ
apply f args = foldl appE f args


applyTypesAcc :: [Type] -> Type -> Type
applyTypesAcc [] acc = acc
applyTypesAcc (t:ts) acc = applyTypesAcc ts (AppT t acc)

-- when building fields of trie, one needs to add the val - variable
-- this is done by this helper function
addVal :: [Type] -> Type -> [Type]
addVal [] valtype     = [AppT (ConT ''Maybe) valtype]
addVal [t] valtype    = [AppT t valtype]
addVal (t:ts) valtype = t : addVal ts valtype



-- this function lifts the type (map val -> map val) to
-- (Maybe (map val) -> Maybe (map val))
-- for use with alter-continuation
lift1 :: KeyMap key map
      => (map val -> map val) -> Maybe (map val) -> Maybe (map val)
lift1 f =  trieToMaybe . f . maybe KeyMap.empty id


-- this function lifts the type (map val -> map val' -> map val'') to
-- (Maybe (map val) -> Maybe (map val') -> Maybe (map val''))
-- for use with combine
lift2 :: KeyMap key map
      => (map val -> map val' -> map val'')
      -> Maybe (map val) -> Maybe (map val') -> Maybe (map val'')
lift2 f mx my
  = maybe (my >>= trieToMaybe . f KeyMap.empty)
          (trieToMaybe . flip f (maybe KeyMap.empty id my))
          mx



--tidym :: KeyMap key map => map val -> Maybe (map val)
--tidym t = if KeyMap.null t then Nothing else Just t

tidy :: KeyMap key map => map val -> map val
tidy m = if KeyMap.null m then KeyMap.empty else m

trieToMaybe :: KeyMap key map => map val -> Maybe (map val)
trieToMaybe t = if KeyMap.null t then Nothing else Just t



ensureTrie :: KeyMap key map => Maybe (map val) -> map val
ensureTrie m = fromMaybe KeyMap.empty m

maybe2trie :: ExpQ
maybe2trie = [| \ mt -> fromMaybe KeyMap.empty mt |]


-- generates the declaration for the helper-function lookup1,lookup2,..
-- according to n
lookupnD :: Int -> DecQ
lookupnD 0 = do
 let lookupName = mkName "lookup0"
 (funD lookupName [clause [] (normalB (varE 'id)) []])
lookupnD n = do
  kvarNames <- mapM newName (replicate n "key")
  mvarName <- newName "m"
  tmpvarNames <- mapM newName (take (n-1) (repeat "x"))
  let argNames = kvarNames ++ [mvarName]
      args = map varP argNames
      lookups =
          map (\ (n1,n2,k) -> bindS (varP n2) (apply (varE 'KeyMap.lookup)
                                                     [varE k,varE n1]))
              (zip3 (mvarName:tmpvarNames) tmpvarNames kvarNames)
      lookupName = mkName ("lookup" ++ show n)
  (funD lookupName
        [clause args
                (normalB (doE (lookups ++
                               [noBindS (apply (varE 'KeyMap.lookup)
                                               [varE (last kvarNames),
                                                varE (last (mvarName:tmpvarNames))])]))) []])


-- generates the declaration for the helper-function alter1,alter2,..
-- according to n
alternD :: Int -> DecQ
alternD 0 = do
    -- let alterName = mkName "alter0"
    [alterDec] <- [d| alter0 = id |]
    return alterDec
alternD 1 = do
  [alterDec] <- [d| alter1 k f m = {-tidy-} (KeyMap.alter k f m) |]
  return alterDec
alternD n = do
  let alterName = mkName ("alter"++show n)
      --alter1Name = mkName "alter1"
      alternMinus1Name = mkName ("alter"++show (n-1))
  kvarNames <- mapM newName (replicate n "key")
  mvarName <- newName "m"
  fvarName <- newName "f"
  let argNames = kvarNames ++ [fvarName,mvarName]
      args = map varP argNames
      kvars = map varE kvarNames
      continuation = [| trieToMaybe .
                        $(apply (varE 'KeyMap.alter)
                                [last kvars,varE fvarName])
                        . ensureTrie |]
  (funD alterName
        [clause args (normalB (apply (varE alternMinus1Name)
                                     (take (n-1) kvars ++
                                      [continuation,varE mvarName])))  []])


-- generates the declaration for the helper-function combine1,combine2,..
-- according to n
combinenD :: Int -> Q [Dec]
combinenD 0 =
  [d| combine0 :: (Maybe val -> Maybe val' -> Maybe val'') -> Maybe val -> Maybe val' -> Maybe val'';combine0 f = f |]

combinenD 1 = do
  [d| combine1 :: KeyMap key map => (Maybe val -> Maybe val' -> Maybe val'') ->  (map val) -> (map val') -> (map val'');combine1 f ma mb = {-tidy-} (KeyMap.combine f ma mb) |]

combinenD n =
  do
   let combineName = mkName ("combine"++show n)
   valNames <- mapM newName (replicate 3 "val")
   keyNames <- mapM newName (replicate n "key")
   mapNames <- mapM newName (replicate n "map")

   fname  <- newName "f"
   maname <- newName "ma"
   mbname <-  newName "mb"
   --trace ("valNames: " ++ show valNames) (return ())
   let context = map (\ (k,m) -> (ClassP ''KeyMap [k, m]))
                     (zip (map VarT keyNames) (map VarT mapNames))
  --   f = (Maybe val -> Maybe val' -> Maybe val'')
       f = AppT (AppT ArrowT (AppT (ConT ''Maybe) (VarT (valNames!!0))))
                (AppT (AppT ArrowT (AppT (ConT ''Maybe) (VarT (valNames!!1))))
 	             (AppT (ConT ''Maybe) (VarT (valNames!!2))))
       args = map (mkArg mapNames) valNames
       singleTypes = addVal (map (AppT ArrowT) (take 2 args)) (args!!2)
       sigType =
        AppT (AppT ArrowT f)
             (applyTypesAcc (tail (reverse singleTypes)) (head (reverse singleTypes)))
       sig  = SigD combineName
                   (ForallT (map PlainTV $ keyNames ++ mapNames ++ valNames) context sigType)
       [fvar,mavar,mbvar] = [varE fname,varE maname,varE mbname]
       continuation =
           [| \ ma mb -> trieToMaybe ($(varE 'KeyMap.combine) $fvar
                                                              (ensureTrie ma)
                                                              (ensureTrie mb))|]
   def <- (funD combineName
                [clause [varP fname,varP maname,varP mbname]
                        (normalB [| $(combinen (n-1)) $continuation $mavar
                                                                    $mbvar|])
                        []])
   return [sig,def]
  where mkArg :: [Name] -> Name -> Type
        mkArg mapNames valName =
           let singleTypes = addVal (map VarT mapNames) (VarT valName)
            in (applyTypesAcc (tail (reverse singleTypes))
                              (head (reverse singleTypes)))


combinen :: Int -> ExpQ
combinen n = varE (mkName ("combine" ++ show n))

mapMaybeWithKeynD :: Int -> DecQ
mapMaybeWithKeynD 0 = do
  [dec] <- [d|
    mapMaybeWithKey0 _ Nothing  _ = Nothing
    mapMaybeWithKey0 k (Just v) f = f k v
    |]
  return dec
mapMaybeWithKeynD n = do
  let fname = mkName $ "mapMaybeWithKey" ++ show n
  kf    <- newName "kf"
  t     <- newName "t"
  f     <- newName "f"

  let e = mapMaybeWithKeynD' n (varE t) (varE f) (varE kf) []

  funD fname [clause [varP kf, varP t, varP f] (normalB e) []]

mapMaybeWithKeynD' 1 t f kf ks =
  [| KeyMap.mapMaybeWithKey (\ k v -> $f ($(apply kf $ reverse ks) k) v) $t
    |]
mapMaybeWithKeynD' m t f kf ks = do
  k <- newName "k"
  v <- newName "v"
  let kp = varP k
      ke = varE k
      vp = varP v
      ve = varE v

  apply [| KeyMap.mapMaybeWithKey |]
        [lamE [kp, vp] $ appE [| Just |] $
                mapMaybeWithKeynD' (m - 1) ve f kf $ ke : ks, t]
        

{-
  [|  (\ k $vp -> Just $ $(mapMaybeWithKeynD (m - 1) $v $f $kf))
    |]
-}

-- generates the toList-method of KeyMap
gen_toList :: [Dec] -> Dec -> Dec -> Q Dec
gen_toList knownDecs keyDec (NewtypeD _ _ _ con _) = do
    (triepat,[trievarname]) <- mkConPattern con
    let m = varE trievarname
    funD (mkName "toList") [clause [triepat] (normalB [| KeyMap.toList $m|]) []]
gen_toList knownDecs keyDec trieDec@(DataD _ _ _ _ _) = do
   noTrieCon       <- getNoTrieCon knownDecs trieDec
   nonEmptyTrieCon <- getNonEmptyTrieCon knownDecs trieDec
   ns <- getNs keyDec
   emptyTrieClause <- clause [conP (getNameOfCon noTrieCon) []]
                             (normalB (conE '[])) []
   nonEmptyTrieClause <- gen_toListClause nonEmptyTrieCon keyDec
   return (FunD (mkName "toList") [emptyTrieClause,nonEmptyTrieClause])


gen_toListClause :: Con -> Dec -> Q Clause
gen_toListClause trieCon@(NormalC triename _) keyDec  = do
      (triePat,trieVarNames) <- mkConPattern trieCon
      ns <- getNs keyDec
      let toLists = map (varE . mkName . ("toList"++) . show) ns
          recCalls = zipWith appE toLists (map varE trieVarNames)

      clause [triePat]
             (normalB (foldr appE
                             (last recCalls)
                             (init (map (appE (varE '(++))) recCalls))))
             []
gen_toListClause tc _ =
    error ("Error:gen_toListClause: malformed trie constructor: " ++ show tc)

{-
toListn2 :: (KeyMap ak am, KeyMap bk bm) => (am (bm val)) -> [val]
toListn2 m = concatMap KeyMap.toList (KeyMap.toList m)

toListn3 m = concatMap KeyMap.toList (concatMap KeyMap.toList (KeyMap.toList m))
-}
-- generates the declaration for the helper-function toList1,toList2,..
-- according to n
toListnD :: Int ->  DecQ
toListnD 0 = do [toList0Dec] <- [d| toList0   = maybe [] (\mx -> [mx]) |]
                return toList0Dec
toListnD 1 = do [toList1Dec] <- [d| toList1 m = KeyMap.toList m |]
                return toList1Dec

toListnD n = do
  mName <- newName "m"
  let toListName = mkName ("toList" ++ show n)
      args = [varP mName]
      concatMaps = replicate (n-1) (appE (varE 'concatMap)
                                         (varE 'KeyMap.toList))
      body = normalB (foldr appE
                            (appE (varE (mkName "toList1")) (varE mName))
                            concatMaps)
  funDec <- funD toListName [clause args body []]
  return funDec



isNewtypeD :: Dec -> Bool
isNewtypeD (NewtypeD _ _ _ _ _) = True
isNewtypeD _ = False