module Data.Comp.Param.Derive.Show
    (
     PShow(..),
     ShowD(..),
     makeShowD
    ) where
import Data.Comp.Derive.Utils
import Data.Comp.Param.FreshM
import Control.Monad
import Language.Haskell.TH hiding (Cxt, match)
class PShow a where
    pshow :: a -> FreshM String
class ShowD f where
    showD :: PShow a => f Var a -> FreshM String
makeShowD :: Name -> Q [Dec]
makeShowD fname = do
  
  
  
  TyConI (DataD _ name args constrs _) <- abstractNewtypeQ $ reify fname
  
  let coArg :: Name = tyVarBndrName $ last args
  
  let conArg :: Name = tyVarBndrName $ last $ init args
  
  let argNames = map (VarT . tyVarBndrName) (init $ init args)
  
  let complType = foldl AppT (ConT name) argNames
  
  let classType = AppT (ConT ''ShowD) complType
  
  constrs' :: [(Name,[Type])] <- mapM normalConExp constrs
  showDDecl <- funD 'showD (map (showDClause conArg coArg) constrs')
  let context = map (\arg -> ClassP ''Show [arg]) argNames
  return [InstanceD context classType [showDDecl]]
      where showDClause :: Name -> Name -> (Name,[Type]) -> ClauseQ
            showDClause conArg coArg (constr, args) = do
              varXs <- newNames (length args) "x"
              
              let patx = ConP constr $ map VarP varXs
              body <- showDBody (nameBase constr) conArg coArg (zip varXs args)
              return $ Clause [patx] (NormalB body) []
            showDBody :: String -> Name -> Name -> [(Name, Type)] -> ExpQ
            showDBody constr conArg coArg x =
                [|liftM (unwords . (constr :) .
                         map (\x -> if elem ' ' x then "(" ++ x ++ ")" else x))
                        (sequence $(listE $ map (showDB conArg coArg) x))|]
            showDB :: Name -> Name -> (Name, Type) -> ExpQ
            showDB conArg coArg (x, tp)
                | not (containsType tp (VarT conArg)) &&
                  not (containsType tp (VarT coArg)) =
                    [| return $ show $(varE x) |]
                | otherwise =
                    case tp of
                      VarT a
                          | a == coArg -> [| pshow $(varE x) |]
                      AppT (AppT ArrowT (VarT a)) _
                          | a == conArg ->
                              [| do {v <- genVar;
                                     body <- pshow $ $(varE x) v;
                                     return $ "\\" ++ show v ++ " -> " ++ body} |]
                      SigT tp' _ ->
                          showDB conArg coArg (x, tp')
                      _ ->
                          if containsType tp (VarT conArg) then
                              [| showD $(varE x) |]
                          else
                              [| pshow $(varE x) |]