{-# OPTIONS --safe --without-K #-}
open import Prelude

module Generics.Reflection.Connection where

open import Utils.Reflection
open import Utils.Error as Err

open import Generics.Description
open import Generics.Recursion  
open import Generics.Algebra

open import Generics.Reflection.Telescope
open import Generics.Reflection.Name
open import Generics.Reflection.Uncurry
open import Generics.Reflection.Recursion

private
  pattern `DataC x y     = def₂ (quote DataC) x y
  pattern `datac a b c d = con₄ (quote datac) a b c d
  pattern `FoldC x y     = def₂ (quote FoldC) x y
  pattern `IndC  x y     = def₂ (quote IndC)  x y
  pattern `Algs  x y     = def₂ (quote Algs)  x y
  pattern `Coalgs x y    = def₂ (quote Coalgs) x y
  pattern `toN            = proj (quote (DataC.toN))
  pattern `fromN          = proj (quote (DataC.fromN))
  pattern `fromN-toN      = proj (quote (DataC.fromN-toN))
  pattern `toN-fromN      = proj (quote (DataC.toN-fromN))
  pattern `FoldC-equation = proj (quote (FoldC.equation))
  pattern `IndC-equation  = proj (quote (IndC.equation))

module _ (pars : ) where
  conToClause : (c : Name)  TC (Telescope × Vars)
  conToClause c = < forgetTypes , cxtToVars 0 (`refl , `refl) > <$> getConTelescope c pars

  consToClauses : (cs : Names)  TC (List (Telescope × Name × Vars))
  consToClauses []       =  [] 
  consToClauses (c  cs) = do
     , (t , p) , args  conToClause c
    cls                  consToClauses cs
    return $ ( , c , (`inl t , `inl p) , args)
       ((λ ( , c , (t , p) , args)   , c , (`inr t , `inr p) , args) <$> cls)

  module _ (cs : Names) where
    genFromCons :  (Telescope × Name × Vars  Clause)  TC Clauses
    genFromCons f = map f <$> consToClauses cs

    genToNT = genFromCons λ where
      ( , c , (_ , p) , args , _)    vArg `toN  [ vArg p ] `=
        con c (hUnknowns pars <> args)

    genFromNT = genFromCons λ where
      (Γ , c , (t , _) , _ , args)  Γ  vArg `fromN  [ vArg (con c args) ] `= t

    genFromN-toNT = genFromCons λ where
      (Γ , _ , (_ , p) , _)  Γ  vArg `fromN-toN  [ vArg p ] `= `refl

    genToN-fromNT = genFromCons λ where
      (Γ , c , _ , _ , args)  Γ  vArg `toN-fromN  [ vArg (con c args) ] `= `refl

    genFoldC-equation = pat-lam₀ <$> genFromCons λ where
      (Γ , c , (_ , p) , _)  Γ  vArg `FoldC-equation  vArg p  [] `= `refl

    genIndC-equation = pat-lam₀ <$> genFromCons λ where
      (Γ , c , (_ , p) , _)  Γ  vArg `IndC-equation  vArg p  [] `= `refl
  
genDataCT : (D : DataD) (N : DataT D)  Tactic
genDataCT D N hole = do
  `D  quoteωTC D
  `N  quoteωTC N

  `Ds  formatErrorParts [ termErr `D ]
  `Ns  formatErrorParts [ termErr `N ]
  let msg = "<An instance of DataC " <> `Ds <> " " <> `Ns <> ">"
  dataC  freshName msg

  d  DataToNativeName  D N
  pars , cs  getDataDefinition d

  toN        genToNT       pars cs
  fromN      genFromNT     pars cs
  fromN-toN  genFromN-toNT pars cs 
  toN-fromN  genToN-fromNT pars cs 

  noConstraints $ define (vArg dataC) (`DataC `D `N) (toN <> fromN <> fromN-toN <> toN-fromN)
  unify hole (def₀ dataC)
  where open DataD D

genFoldCT' : (P : FoldP) (f : FoldT P)  Tactic
genFoldCT' P f hole = do
  `P  quoteωTC P
  `f  quoteωTC f
  hole  checkType hole $ `FoldC `P `f

  d  FoldPToNativeName P
  pars , cs  getDataDefinition d

  genFoldC-equation pars cs  >>= unify hole

genFoldCT : (P : FoldP)  Name  Tactic
genFoldCT P d hole = do
  `P  quoteωTC P
  `t  uncurryFoldP P d
  hole  checkType hole $ `FoldC `P `t

  d  FoldPToNativeName P
  pars , cs  getDataDefinition d

  genFoldC-equation pars cs >>= unify hole

genIndCT' : (P : IndP) (f : IndT P)  Tactic
genIndCT' P f hole = do
  `P  quoteωTC P
  `f  quoteωTC f 
  hole  checkType hole $ `IndC `P `f

  d  IndPToNativeName P
  pars , cs  getDataDefinition d

  genIndC-equation pars cs  >>= unify hole

genIndCT : (P : IndP)  Name  Tactic
genIndCT P d hole = do
  `P  quoteωTC P
  `t  uncurryIndP P d
  hole  checkType hole $ `IndC `P `t

  d  IndPToNativeName P
  pars , cs  getDataDefinition d
  
  genIndC-equation pars cs >>= unify hole

macro
  genDataC : (D : DataD) (N : DataT D)  Tactic
  genDataC = genDataCT

  genFoldC' : (P : FoldP) (f : FoldT P)  Tactic
  genFoldC' = genFoldCT'

  genFoldC : (P : FoldP)  Name  Tactic
  genFoldC = genFoldCT

  genIndC' : (P : IndP) (f : IndT P)  Tactic
  genIndC' = genIndCT'

  genIndC : (P : IndP)  Name  Tactic
  genIndC = genIndCT