{-# OPTIONS --safe --without-K #-}
open import Prelude
  hiding ([_,_])

module Utils.Reflection.Tactic where

open import Utils.Reflection.Core
open import Utils.Reflection.Show
open import Utils.Reflection.Term
import Utils.Error as Err

private variable
  A : Set 

dprint = debugPrint "meta" 5

give : Term  Tactic
give v = λ hole  unify hole v

define : Arg Name  Type  Clauses  TC 
define f a cs = declareDef f a >> defineFun (unArg f) cs

define! : Arg Type  Clauses  TC Name
define! (arg i a) cs = do
  f  freshName "_"
  define (arg i f) a cs
  return f

extend*Context : Telescope  TC A  TC A
extend*Context []              m = m
extend*Context ((s , a)  tel) m = extendContext s a (extend*Context tel m)

_onClause_ : (Term  TC Term)  Clause  TC Clause
f onClause (tel  ps `= t) = do
  u  extend*Context tel (f t)
  return $ tel  ps `= u
f onClause cl = return cl

_onClauses_ : (Term  TC Term)  Clauses  TC Clauses
_onClauses_ f = mapM (f onClause_)

checkClauses : Clauses  Type  TC Clauses
checkClauses cls `A = do
  pat-lam₀ cls  checkType (pat-lam₀ cls) `A
    where _  Err.IMPOSSIBLE
  return cls
  
quoteTC! : A  TC Term
quoteTC! a = withNormalisation true (quoteTC a)

quoteωTC! : {A : Setω}  A  TC Term
quoteωTC! a = withNormalisation true (quoteωTC a)

newMeta : Type  TC Term
newMeta = checkType unknown

newMeta! : TC Term
newMeta! = newMeta unknown

typeErrorS : String  TC A
typeErrorS s = typeError (strErr s  [])

blockOnMeta! : Meta  TC A
blockOnMeta! x = commitTC >>= λ _  blockOnMeta x

inferNormalisedType : Term  TC Type
inferNormalisedType t = withNormalisation true (inferType t)

formatErrorPart : ErrorPart  TC String
formatErrorPart = formatErrorParts  [_]

defineUnify : Arg String  Type  Term  Term  TC Name
defineUnify (arg i ns) ty tm hole = do
  n  freshName ns
  declareDef (arg i n) ty
  defineFun n [ []  [] `= tm ]

  unify hole (def₀ n)

  return n
  
evalTC : TC A  Tactic
evalTC {A = A} c hole = do
  v   c
  `v  quoteTC v
  `A  quoteTC A
  checkedHole  checkType hole `A
  unify checkedHole `v

macro
  evalT : TC A  Tactic
  evalT = evalTC

-- Typed version of extendContext
exCxtT : String  ArgInfo  (B : Set )
   (Type  B  TC A)  TC A
exCxtT s i B f = do
  `B  quoteTC! B
  extendContext s (arg i `B) do
    x  unquoteTC {A = B} (var₀ 0)
    f `B x

getConTelescope : Name  (pars : )  TC Telescope
getConTelescope c pars = drop pars  fst  (coerce'_to (Telescope × Type)) <$> getType c

getAbsName : {A : Set } {B : A  Set ℓ′}  ((x : A)  B x)  TC String
getAbsName f = caseM quoteTC! f of λ { (lam visible (abs s _))  return s ; t  Err.notλ t }

getAbsNameω : {A : Set } {B : A  Setω}  ((x : A)  B x)  TC String
getAbsNameω f = caseM quoteωTC! f of λ { (lam visible (abs s _))  return s ; t  Err.notλ t }

getFunction : Name  TC (Type × Clauses)
getFunction d = do
  function cs  getDefinition d
    where t  Err.notFun d
  t  getType d
  return $ t , cs

getDataDefinition : Name  TC ( × Names)
getDataDefinition d = do
  data-type pars cs  getDefinition d
    where _  Err.notData (def₀ d)
  return $ pars , cs

getTelescope : Name  TC (Telescope × Type)
getTelescope s =   (getType s) 

macro
  getTelescopeT : Name  Tactic
  getTelescopeT s = evalTC $ getTelescope s

getSetLevel : Type  TC Term
getSetLevel (agda-sort (set t)) = return t
getSetLevel (`Set n) = quoteTC (fromℕ n)
  where 
    fromℕ :   Level
    fromℕ zero = lzero
    fromℕ (suc n) = lsuc (fromℕ n)
getSetLevel (def (quote Set) []) = return (quoteTerm lzero)
getSetLevel (def (quote Set) [ arg _ x ]) = return x
getSetLevel t = quoteTC t >>= λ t 
                  typeError [ strErr $ showTerm t <> " level error!" ]


-- Rename names in a telescope to the first letter of the given type,
-- if no name is given
renameUnderscore : Telescope  TC Telescope
renameUnderscore []        = return []
renameUnderscore (("_" , x@(arg visible-relevant-ω `A))  as) = do
  s  formatErrorPart $ termErr `A
  -- let s = ⇑ [ maybe′ toLower 'x' $ head (⇑ s ⦂ List Char) ]
  extendContext s x $ (("x" , x) ∷_) <$> renameUnderscore as
renameUnderscore (a@(s , x)  as) = extendContext s x $ a ∷_ <$> renameUnderscore as