{-# OPTIONS --without-K --safe #-}

module Fragment.Tactic.Utils where

open import Reflection hiding (name; Type; _≟_)
open import Reflection.Term using (_≟_)

open import Data.Nat using (; zero; suc)
open import Data.Fin using (Fin)
open import Data.Nat.Show using (show)
open import Data.String using (String) renaming (_++_ to _⟨S⟩_)
open import Data.List using (List; []; _∷_; _++_; drop; take; reverse)
open import Data.Vec using (Vec; []; _∷_; map; toList)
open import Data.Bool using (Bool; true; false; if_then_else_)
open import Data.Maybe using (Maybe; nothing; just)
open import Data.Product using (_×_; _,_)

open import Relation.Nullary using (yes; no; _because_)
open import Relation.Binary.PropositionalEquality as PE using (_≡_)

vra :  {a} {A : Set a}  A  Arg A
vra = arg (arg-info visible relevant)

hra :  {a} {A : Set a}  A  Arg A
hra = arg (arg-info hidden relevant)

infixr 5 λ⦅_⦆→_ λ⦃_⦄→_

λ⦅_⦆→_ : String  Term  Term
λ⦅ x ⦆→ body = lam visible (abs x body)

λ⦃_⦄→_ : String  Term  Term
λ⦃ x ⦄→ body = lam hidden (abs x body)

constructors : Definition  List Name
constructors (data-type _ cs) = cs
constructors _                = []

apply : Term  List (Arg Term)  Term
apply (var x xs) args      = var x (xs ++ args)
apply (con x xs) args      = con x (xs ++ args)
apply (def x xs) args      = def x (xs ++ args)
apply (meta x xs) args     = meta x (xs ++ args)
apply (pat-lam cs xs) args = pat-lam cs (xs ++ args)
apply x _                  = x

prod :  {a} {A : Set a}    List A  List A
prod n xs = reverse (drop n (reverse xs))

ekat :  {a} {A : Set a}    List A  List A
ekat n xs = reverse (take n (reverse xs))

find :  {a} {A : Set a}  (A  TC Bool)  List A  TC (Maybe A)
find p [] = return nothing
find p (x  xs)
  = do p?  p x
       if p? then return (just x)
             else find p xs

mapList :  {a b} {A : Set a} {B : Set b}
           List (A  B)  List A  List B
mapList [] _              = []
mapList _ []              = []
mapList (f  fs) (x  xs) = f x  mapList fs xs

unapply : Term    Term
unapply (var x args) n      = var x (prod n args)
unapply (con x args) n      = con x (prod n args)
unapply (def x args) n      = def x (prod n args)
unapply (meta x args) n     = meta x (prod n args)
unapply (pat-lam cs args) n = pat-lam cs (prod n args)
unapply x _                 = x

equalTypes : Term  Term  Bool
equalTypes τ τ' with τ  τ'
...                | yes _ = true
...                | _     = false

flatten :  {a} {A : Set a}  TC (TC A)  TC A
flatten x
  = do x'  x
       x'

listTC :  {a} {A : Set a}  List (TC A)  TC (List A)
listTC []       = return []
listTC (x  xs)
  = do x'  x
       xs'  listTC xs
       return (x'  xs')

n-ary :  (n : )  Term  Term
n-ary zero body    = body
n-ary (suc n) body = λ⦅ "x" ⟨S⟩ show n ⦆→ (n-ary n body)

debrujin :  (n : )  Vec Term n
debrujin zero    = []
debrujin (suc n) = (var n [])  debrujin n

η-convert :  (n : )  Term  TC Term
η-convert n t = runSpeculative inner
  where inner : TC (Term × Bool)
        inner
          = do t  normalise (n-ary n (apply t (toList (map vra (debrujin n)))))
               return (t , false)

prefix :   Term  Term  TC Bool
prefix n x y
  = do y  catchTC (η-convert n (unapply y n)) (return y)
       let (b because _) = x  y
       return b

extract-type-arg : Term  TC Term
extract-type-arg (pi (arg _ x) _) = return x
extract-type-arg x                = typeError (termErr x  strErr "isn't a pi type"  [])

extract-name : Term  TC Name
extract-name (def x _) = return x
extract-name x         = typeError (termErr x  strErr "isn't an application of a definition"  [])

extract-definition : Term  TC Definition
extract-definition x
  = do x'  normalise x
       η  extract-name x'
       getDefinition η

extract-constructors : Term  TC (List Name)
extract-constructors x
  = do δ  extract-definition x
       return (constructors δ)

fin :   Term
fin zero    = con (quote Fin.zero) []
fin (suc n) = con (quote Fin.suc) (vra (fin n)  [])

vec :  {n : }  Vec Term n  Term
vec []       = con (quote Vec.[]) []
vec (x  xs) = con (quote Vec._∷_) (vra x  vra (vec xs)  [])

vec-len : Term  TC 
vec-len (def (quote Vec) (_  _  (arg _ n)  [])) = unquoteTC n
vec-len t = typeError (termErr t  strErr "isn't a"  nameErr (quote Vec)  [])

panic :  {a} {A : Set a}  Term  TC A
panic x = typeError (termErr x  [])

strip-telescope : Term  TC (Term × List (Arg String))
strip-telescope (pi (vArg _) (abs s x))
  = do (τ , acc)  strip-telescope x
       return (τ , vra s  acc)
strip-telescope (pi (hArg _) (abs s x))
  = do (τ , acc)  strip-telescope x
       return (τ , hra s  acc)
strip-telescope (pi _ _) =
  typeError (strErr "no support for irrelevant goals"  [])
strip-telescope t        = return (t , [])

restore-telescope : List (Arg String)  Term  TC Term
restore-telescope [] t = return t
restore-telescope (vArg x  xs) t
  = do t  restore-telescope xs t
       return (λ⦅ x ⦆→ t)
restore-telescope (hArg x  xs) t
  = do t  restore-telescope xs t
       return (λ⦃ x ⦄→ t)
restore-telescope (_  _) _ =
  typeError (strErr "no support for irrelevant goals"  [])

hasType : Term  Term  TC Bool
hasType τ t = runSpeculative inner
  where inner = do τ'  inferType t
                   return (equalTypes τ τ' , false)

insert : List Term  Term  List Term
insert [] n       = n  []
insert (x  xs) n with x  n
...                  | yes _ = x  xs
...                  | no _  = x  insert xs n

indexOf : List Term  Term  Maybe 
indexOf [] t = nothing
indexOf (x  xs) t with x  t | indexOf xs t
...                   | yes _ | _       = just 0
...                   | no _  | just n  = just (suc n)
...                   | no _  | nothing = nothing