{-# LANGUAGE FlexibleInstances     #-}
{-# LANGUAGE ConstraintKinds       #-}
{-# LANGUAGE TupleSections         #-}
{-# LANGUAGE RankNTypes            #-}
{-# LANGUAGE TypeOperators         #-}
{-# LANGUAGE DataKinds             #-}
{-# LANGUAGE PolyKinds             #-}
{-# LANGUAGE GADTs                 #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE ScopedTypeVariables   #-}
{-# OPTIONS_GHC -Wno-orphans       #-}
module Generics.MRSOP.HDiff.Holes where
import Data.Proxy
import Data.Functor.Const
import Data.Type.Equality
import Control.Monad.Identity
import qualified Data.Text.Prettyprint.Doc as PP
import Generics.MRSOP.Util
import Generics.MRSOP.Base
import Generics.MRSOP.Holes
import Generics.MRSOP.HDiff.Renderer
holesPretty :: forall ki fam codes f at ann
             . (HasDatatypeInfo ki fam codes , RendererHO ki)
            => Proxy fam
            -> (PP.Doc ann -> PP.Doc ann) 
            -> (forall at' . f at' -> PP.Doc ann)
            -> Holes ki codes f at
            -> PP.Doc ann
holesPretty _pfam sty sx  (Hole _ x)
  = sty $ sx x
holesPretty _pfam sty _sx (HOpq _ k)
  = sty $ renderHO k
holesPretty pfam sty sx utx@(HPeel _ c rest)
  = renderNP pfam sty (holesSNat utx) c
  $ mapNP (Const . holesPretty pfam sty sx) rest
holesZipRep :: (MonadPlus m)
            => Holes ki codes f at
            -> NA ki (Fix ki codes) at
            -> m (Holes ki codes (f :*: NA ki (Fix ki codes)) at)
holesZipRep (Hole a i) x  = return $ Hole a (i :*: x)
holesZipRep (HOpq a k)  _ = return $ HOpq a k
holesZipRep (HPeel a c d) (NA_I x)
  | Tag cx dx <- sop (unFix x)
  = case testEquality c cx of
      Nothing   -> mzero
      Just Refl -> HPeel a cx <$> mapNPM (uncurry' holesZipRep) (zipNP d dx)
class HasIKProjInj (ki :: kon -> *) (f :: Atom kon -> *) where
  konInj  :: ki k -> f ('K k)
  varProj :: Proxy ki -> f x -> Maybe (IsI x)
instance (HasIKProjInj ki phi) => HasIKProjInj ki (Holes ki codes phi) where
  konInj                   = HOpq'
  varProj pr (Hole _ h)    = varProj pr h
  varProj _  (HPeel _ _ _) = Just IsI
  varProj _  (HOpq _ _)    = Nothing
data IsI :: Atom kon -> * where
  IsI :: (IsNat i) => IsI ('I i)
getIsISNat :: IsI ('I i) -> SNat i
getIsISNat IsI = getSNat (Proxy :: Proxy i)
type HolesTestEqualityCnstr ki f
  = (TestEquality ki , TestEquality f , HasIKProjInj ki f)
instance (HolesTestEqualityCnstr ki f)
    => TestEquality (Holes ki codes f) where
  testEquality (HOpq _ kx) (HOpq _ ky)
    = testEquality kx ky >>= return . apply (Refl :: 'K :~: 'K)
  testEquality (Hole _ v) (Hole _ u)
    = testEquality v u
  testEquality (HOpq _ kx) (Hole _ v)
    = testEquality (konInj kx) v
  testEquality (Hole _ v) (HOpq _ ky)
    = testEquality v (konInj ky)
  testEquality x@(HPeel _ _ _) (Hole _ u)
    = do i@IsI <- varProj (Proxy :: Proxy ki) u
         Refl  <- testEquality (holesSNat x) (getIsISNat i)
         return Refl
  testEquality (Hole _ u) x@(HPeel _ _ _)
    = do i@IsI <- varProj (Proxy :: Proxy ki) u
         Refl  <- testEquality (holesSNat x) (getIsISNat i)
         return Refl
  testEquality x@(HPeel _ _ _) y@(HPeel _ _ _)
    = do Refl <- testEquality (holesSNat x) (holesSNat y)
         return Refl
  testEquality (HOpq _ _) (HPeel _ _ _) = Nothing
  testEquality (HPeel _ _ _) (HOpq _ _) = Nothing