{-# LANGUAGE AllowAmbiguousTypes #-} -- For hideableShow
-- | This module enables to 'hideName'
-- to get reproductible dumps of TemplateHaskell slices.
module Language.Haskell.TH.HideName where

import Data.Bifunctor (bimap)
import Data.Maybe (Maybe (..))
import Data.Bool (Bool(..))
import Data.Function (id)
import Data.Functor ((<$>))
import Language.Haskell.TH
import Language.Haskell.TH.Syntax
import Prelude (undefined)

-- ** Type 'HideableName'
-- | Useful on golden unit tests because 'Name's
-- change often when changing unrelated source code
-- or even when changing basic GHC or executable flags.
class HideableName (showNames::Bool) where
  hideableName :: HideName a => a -> a
-- | Like 'id'.
instance HideableName 'True where
  hideableName = id
-- | Like 'hideName'.
instance HideableName 'False where
  hideableName = hideName

-- * Class 'HideName'
class HideName a where
  -- | Map all 'Name's to a constant in order to overcome
  -- cases where resetting 'TH.counter' is not enough
  -- to get deterministic 'TH.Name's.
  hideName :: a -> a
instance HideName a => HideName (Maybe a) where
  hideName = (hideName <$>)
instance (HideName a, HideName b) => HideName (a,b) where
  hideName = bimap hideName hideName
instance HideName Body where
  hideName (GuardedB gs) = GuardedB ((\(g, e) -> (hideName g, hideName e)) <$> gs)
  hideName (NormalB e) = NormalB (hideName e)
instance HideName Clause where
  hideName (Clause ps b ds) = Clause (hideName <$> ps) (hideName b) (hideName <$> ds)
instance HideName Dec where
  hideName (FunD f cs) = FunD (hideName f) (hideName <$> cs)
  hideName (ValD p r ds) = ValD (hideName p) (hideName r) (hideName <$> ds)
  -- FIXME: completing is not likely useful since Symantic.Parser is only using __Typed__ Template Haskell
  --hideName (DataD cxt n bs mk cs dcs) = DataD (hideName cxt) (hideName n) (hideName bs) (hideName mk) (hideName cs) (hideName dcs)
  hideName _ = undefined
--   | NewtypeD Cxt Name [TyVarBndr ()]
--              (Maybe Kind)         -- Kind signature
--              Con [DerivClause]    -- ^ @{ newtype Cxt x => T x = A (B x)
--                                   --       deriving (Z,W Q)
--                                   --       deriving stock Eq }@
--   | TypeDataD Name [TyVarBndr ()]
--           (Maybe Kind)            -- Kind signature (allowed only for GADTs)
--           [Con]                   -- ^ @{ type data T x = A x | B (T x) }@
--   | TySynD Name [TyVarBndr ()] Type -- ^ @{ type T x = (x,x) }@
--   | ClassD Cxt Name [TyVarBndr ()]
--          [FunDep] [Dec]           -- ^ @{ class Eq a => Ord a where ds }@
--   | InstanceD (Maybe Overlap) Cxt Type [Dec]
--                                   -- ^ @{ instance {\-\# OVERLAPS \#-\}
--                                   --        Show w => Show [w] where ds }@
--   | SigD Name Type                -- ^ @{ length :: [a] -> Int }@
--   | KiSigD Name Kind              -- ^ @{ type TypeRep :: k -> Type }@
--   | ForeignD Foreign              -- ^ @{ foreign import ... }
--                                   --{ foreign export ... }@
-- 
--   | InfixD Fixity Name            -- ^ @{ infix 3 foo }@
--   | DefaultD [Type]               -- ^ @{ default (Integer, Double) }@
-- 
--   -- | pragmas
--   | PragmaD Pragma                -- ^ @{ {\-\# INLINE [1] foo \#-\} }@
-- 
--   -- | data families (may also appear in [Dec] of 'ClassD' and 'InstanceD')
--   | DataFamilyD Name [TyVarBndr ()]
--                (Maybe Kind)
--          -- ^ @{ data family T a b c :: * }@
-- 
--   | DataInstD Cxt (Maybe [TyVarBndr ()]) Type
--              (Maybe Kind)         -- Kind signature
--              [Con] [DerivClause]  -- ^ @{ data instance Cxt x => T [x]
--                                   --       = A x | B (T x)
--                                   --       deriving (Z,W)
--                                   --       deriving stock Eq }@
-- 
--   | NewtypeInstD Cxt (Maybe [TyVarBndr ()]) Type -- Quantified type vars
--                  (Maybe Kind)      -- Kind signature
--                  Con [DerivClause] -- ^ @{ newtype instance Cxt x => T [x]
--                                    --        = A (B x)
--                                    --        deriving (Z,W)
--                                    --        deriving stock Eq }@
--   | TySynInstD TySynEqn            -- ^ @{ type instance ... }@
-- 
--   -- | open type families (may also appear in [Dec] of 'ClassD' and 'InstanceD')
--   | OpenTypeFamilyD TypeFamilyHead
--          -- ^ @{ type family T a b c = (r :: *) | r -> a b }@
-- 
--   | ClosedTypeFamilyD TypeFamilyHead [TySynEqn]
--        -- ^ @{ type family F a b = (r :: *) | r -> a where ... }@
-- 
--   | RoleAnnotD Name [Role]     -- ^ @{ type role T nominal representational }@
--   | StandaloneDerivD (Maybe DerivStrategy) Cxt Type
--        -- ^ @{ deriving stock instance Ord a => Ord (Foo a) }@
--   | DefaultSigD Name Type      -- ^ @{ default size :: Data a => a -> Int }@
-- 
--   -- | Pattern Synonyms
--   | PatSynD Name PatSynArgs PatSynDir Pat
--       -- ^ @{ pattern P v1 v2 .. vn <- p }@  unidirectional           or
--       --   @{ pattern P v1 v2 .. vn = p  }@  implicit bidirectional   or
--       --   @{ pattern P v1 v2 .. vn <- p
--       --        where P v1 v2 .. vn = e  }@  explicit bidirectional
--       --
--       -- also, besides prefix pattern synonyms, both infix and record
--       -- pattern synonyms are supported. See 'PatSynArgs' for details
-- 
--   | PatSynSigD Name PatSynType  -- ^ A pattern synonym's type signature.
-- 
--   | ImplicitParamBindD String Exp
--       -- ^ @{ ?x = expr }@
--       --
--       -- Implicit parameter binding declaration. Can only be used in let
--       -- and where clauses which consist entirely of implicit bindings.

instance HideName Exp where
  hideName (AppE e1 e2) = AppE (hideName e1) (hideName e2)
  hideName (AppTypeE e t) = AppTypeE (hideName e) (hideName t)
  hideName (ArithSeqE d) = ArithSeqE (hideName d)
  hideName (CaseE e ms) = CaseE (hideName e) (hideName <$> ms)
  hideName (CompE ss) = CompE (hideName <$> ss)
  hideName (ConE c) = ConE (hideName c)
  hideName (CondE guard true false) = CondE (hideName guard) (hideName true) (hideName false)
  hideName (DoE m ss) = DoE (hideName <$> m) (hideName <$> ss)
  hideName (ImplicitParamVarE n) = ImplicitParamVarE n
  hideName (InfixE e1 op e2) = InfixE (hideName <$> e1) (hideName op) (hideName <$> e2)
  hideName (LabelE s) = LabelE s
  hideName (LamCaseE ms) = LamCaseE (hideName <$> ms)
  hideName (LamE ps e) = LamE (hideName <$> ps) (hideName e)
  hideName (LetE ds e) = LetE (hideName <$> ds) (hideName e)
  hideName (ListE es) = ListE (hideName <$> es)
  hideName (LitE l) = LitE l
  hideName (MDoE m ss) = MDoE (hideName <$> m) (hideName <$> ss)
  hideName (MultiIfE alts) = MultiIfE ((\(g, e) -> (hideName g, hideName e)) <$> alts)
  hideName (ParensE e) = ParensE (hideName e)
  hideName (RecConE nm fs) = RecConE (hideName nm) ((\(n, e) -> (hideName n, hideName e)) <$> fs)
  hideName (RecUpdE e fs) = RecUpdE (hideName e) ((\(n, ee) -> (hideName n, hideName ee)) <$> fs)
  hideName (SigE e t) = SigE (hideName e) (hideName t)
  hideName (StaticE e) = StaticE (hideName e)
  hideName (TupE es) = TupE ((hideName <$>) <$> es)
  hideName (UInfixE e1 op e2) = UInfixE (hideName e1) (hideName op) (hideName e2)
  hideName (UnboundVarE v) = UnboundVarE (hideName v)
  hideName (UnboxedSumE e alt arity) = UnboxedSumE (hideName e) alt arity
  hideName (UnboxedTupE es) = UnboxedTupE ((hideName <$>) <$> es)
  hideName (VarE v) = VarE (hideName v)
  hideName (GetFieldE e n) = GetFieldE (hideName e) n
  hideName (ProjectionE ns) = ProjectionE ns
instance HideName Guard where
  hideName (NormalG e) = NormalG (hideName e)
  hideName (PatG ss) = PatG (hideName <$> ss)
instance HideName Lit where
  hideName x = x
instance HideName Match where
  hideName (Match p b ds) = Match (hideName p) (hideName b) (hideName <$> ds)
instance HideName ModName where
  hideName (ModName n) = ModName n
instance HideName Name where
  -- This is the hidding
  hideName (Name (OccName on) (NameU _u)) = Name (OccName on) NameS
  hideName (Name (OccName on) (NameL _u)) = Name (OccName on) NameS
  hideName (Name on n) = Name (hideName on) (hideName n)
instance HideName NameFlavour where
  hideName (NameG n p m) = NameG n p m
  hideName (NameL n) = NameL n
  hideName (NameQ n) = NameQ n
  hideName NameS = NameS
  hideName (NameU n) = NameU n
instance HideName OccName where
  hideName (OccName n) = OccName n
instance HideName Range where
  hideName (FromR e) = FromR (hideName e)
  hideName (FromThenR f t) = FromThenR (hideName f) (hideName t)
  hideName (FromToR f t) = FromToR (hideName f) (hideName t)
  hideName (FromThenToR f t to) = FromThenToR (hideName f) (hideName t) (hideName to)
instance HideName Stmt where
  hideName (BindS p e) = BindS (hideName p) (hideName e)
  hideName (LetS ds) = LetS (hideName <$> ds)
  hideName (NoBindS e) = NoBindS (hideName e)
  hideName (ParS ss) = ParS ((hideName <$>) <$> ss)
  hideName (RecS ss) = RecS (hideName <$> ss)
instance HideName (TyVarBndr f) where
  hideName (PlainTV n f) = PlainTV (hideName n) f
  hideName (KindedTV n f k) = KindedTV (hideName n) f (hideName k)
instance HideName Type where
  hideName (ForallT vs ctx t) = ForallT (hideName <$> vs) (hideName <$> ctx) (hideName t)
  hideName (ForallVisT vs t) = ForallVisT (hideName <$> vs) (hideName t)
  hideName (AppT t x) = AppT (hideName t) (hideName x)
  hideName (AppKindT t k) = AppKindT (hideName t) (hideName k)
  hideName (SigT t k) = SigT (hideName t) (hideName k)
  hideName (VarT n) = VarT (hideName n)
  hideName (ConT n) = ConT (hideName n)
  hideName (PromotedT n) = PromotedT (hideName n)
  hideName (InfixT x n y) = InfixT (hideName x) (hideName n) (hideName y)
  hideName (UInfixT x n y) = UInfixT (hideName x) (hideName n) (hideName y)
  hideName (ParensT t) = ParensT (hideName t)
  hideName (TupleT x) = TupleT x
  hideName (UnboxedTupleT x) = UnboxedTupleT x
  hideName (UnboxedSumT x) = UnboxedSumT x
  hideName (ArrowT) = ArrowT
  hideName (MulArrowT) = MulArrowT
  hideName (EqualityT) = EqualityT
  hideName (ListT) = ListT
  hideName (PromotedTupleT x) = PromotedTupleT x
  hideName (PromotedNilT) = PromotedNilT
  hideName (PromotedConsT) = PromotedConsT
  hideName (StarT) = StarT
  hideName (ConstraintT) = ConstraintT
  hideName (LitT t) = LitT t
  hideName (WildCardT) = WildCardT
  hideName (ImplicitParamT n t) = ImplicitParamT n (hideName t)
instance HideName Pat where
  hideName (AsP v p) = AsP (hideName v) (hideName p)
  hideName (BangP p) = BangP (hideName p)
  hideName (ConP n ts ps) = ConP (hideName n) (hideName <$> ts) (hideName <$> ps)
  hideName (InfixP p1 n p2) = InfixP (hideName p1) (hideName n) (hideName p2)
  hideName (ListP ps) = ListP (hideName <$> ps)
  hideName (LitP l) = LitP (hideName l)
  hideName (ParensP p) = ParensP (hideName p)
  hideName (RecP nm fs) = RecP (nm) ((\(n,p) -> (hideName n, hideName p)) <$> fs)
  hideName (SigP p t) = SigP (hideName p) (hideName t)
  hideName (TildeP p) = TildeP (hideName p)
  hideName (TupP ps) = TupP (hideName <$> ps)
  hideName (UInfixP p1 n p2) = UInfixP (hideName p1) (hideName n) (hideName p2)
  hideName (UnboxedSumP p alt arity) = UnboxedSumP (hideName p) alt arity
  hideName (UnboxedTupP ps) = UnboxedTupP (hideName <$> ps)
  hideName (VarP v) = VarP (hideName v)
  hideName (ViewP e p) = ViewP (hideName e) (hideName p)
  hideName WildP = WildP
instance HideName a => HideName [a] where
  hideName = (hideName <$>)