content: {-# LANGUAGE ConstraintKinds #-}
  {-# LANGUAGE DataKinds #-}
  {-# LANGUAGE FlexibleContexts #-}
  {-# LANGUAGE FlexibleInstances #-}
  {-# LANGUAGE GADTs #-}
  {-# LANGUAGE MultiParamTypeClasses #-}
  {-# LANGUAGE PolyKinds #-}
  {-# LANGUAGE RankNTypes #-}
  {-# LANGUAGE ScopedTypeVariables #-}
  {-# LANGUAGE TypeApplications #-}
  {-# LANGUAGE TypeFamilies #-}
  {-# LANGUAGE TypeOperators #-}
  {-# LANGUAGE NoImplicitPrelude #-}
  
  module Data.Morpheus.Generic.Gmap
    ( Gmap,
      gmap,
    )
  where
  
  import Data.Morpheus.Generic.Proxy
    ( CProxy (..),
      rep,
    )
  import GHC.Generics
    ( C,
      D,
      Datatype,
      Generic (..),
      K1,
      M1,
      S,
      U1,
      type (:*:),
      type (:+:),
    )
  import Relude
  
  -- newtype GmapFun (fun :: Type -> Constraint) (v :: Type) = GmapFun
  --   { gmapFun :: forall f a. (fun a) => f a -> v
  --   }
  
  gmap :: (Gmap c (Rep a)) => f a -> [CProxy c]
  gmap p = gfmap (rep p)
  
  class Gmap (c :: Type -> Constraint) a where
    gfmap :: f a -> [CProxy c]
  
  instance (Datatype d, Gmap c a) => Gmap c (M1 D d a) where
    gfmap _ = gfmap (Proxy @a)
  
  instance (Gmap con a) => Gmap con (M1 C c a) where
    gfmap _ = gfmap (Proxy @a)
  
  instance (Gmap c a, Gmap c b) => Gmap c (a :+: b) where
    gfmap _ = gfmap (Proxy @a) <> gfmap (Proxy @b)
  
  instance (Gmap c a, Gmap c b) => Gmap c (a :*: b) where
    gfmap _ = gfmap (Proxy @a) <> gfmap (Proxy @b)
  
  instance (c a) => Gmap c (M1 S s (K1 x a)) where
    gfmap _ = [CProxy (Proxy @a)]
  
  instance Gmap c U1 where
    gfmap _ = []
  