open import DynamicallyChecked.Universe
open import Data.Nat

module DynamicallyChecked.BiGUL where

open import DynamicallyChecked.Utilities
open import DynamicallyChecked.Partiality
open import DynamicallyChecked.Lens
open import DynamicallyChecked.Rearrangement
open import DynamicallyChecked.Case as Case

open import Level
open import Data.Product
open import Data.Bool
open import Data.Maybe
open import Data.List
open import Relation.Nullary
open import Relation.Binary.PropositionalEquality


mutual

  data BiGUL {n : } (F : Functor n) : U n  U n  Set₁ where
    fail    : {S V : U n}  BiGUL F S V
    skip    : {S V : U n} (f :  S  (μ F)   V  (μ F))  BiGUL F S V
    replace : {S : U n}  BiGUL F S S
    prod    : {S V S' V' : U n} (b : BiGUL F S V) (b' : BiGUL F S' V')  BiGUL F (S  S') (V  V')
    rearrS  : {S S' V : U n}
              (spat : Pattern F S) (spat' : Pattern F S') (expr : Expr spat spat') (c : CompleteExpr spat spat' expr)
              (b : BiGUL F S' V)  BiGUL F S V
    rearrV  : {S V V' : U n}
              (vpat : Pattern F V) (vpat' : Pattern F V') (expr : Expr vpat vpat') (c : CompleteExpr vpat vpat' expr)
              (b : BiGUL F S V')  BiGUL F S V
    case    : {S V : U n} (branches : List (CaseBranch F S V))  BiGUL F S V

  data CaseBranchType {n : } (F : Functor n) (S V : U n) : Set₁ where
    normal   : (b : BiGUL F S V) (q :  S  (μ F)  Bool)  CaseBranchType F S V
    adaptive : (f :  S  (μ F)   V  (μ F)   S  (μ F))  CaseBranchType F S V

  CaseBranch : {n : } (F : Functor n) (S V : U n)  Set₁
  CaseBranch F S V = ( S  (μ F)   V  (μ F)  Bool) × CaseBranchType F S V

elimCaseBranchType : {n : } {F : Functor n} {S V : U n} {l : Level} {A : Set l} 
                     (BiGUL F S V  ( S  (μ F)  Bool)  A)  (( S  (μ F)   V  (μ F)   S  (μ F))  A) 
                     CaseBranchType F S V  A
elimCaseBranchType n a (normal b q) = n b q
elimCaseBranchType n a (adaptive f) = a f

mutual

  interp : {n : } {F : Functor n} {S V : U n} (b : BiGUL F S V)   S  (μ F)   V  (μ F)
  interp         fail                         = iso-lens empty-iso
  interp         (skip {V = V} f)             = skip-lens (U-dec V) f
  interp         replace                      = iso-lens id-iso
  interp         (prod b b')                  = interp b  interp b'
  interp         (rearrS spat spat' expr c b) = rearrangement-iso spat spat' expr c  interp b
  interp         (rearrV vpat vpat' expr c b) = interp b  sym-iso (rearrangement-iso vpat vpat' expr c)
  interp {F = F} (case {S} {V} branches)      = case-lens ( S  (μ F)) ( V  (μ F)) (interp-CaseBranch branches)

  interp-CaseBranch : {n : } {F : Functor n} {S V : U n} 
                      List (CaseBranch F S V)  List (Case.Branch ( S  (μ F)) ( V  (μ F)))
  interp-CaseBranch []                            = []
  interp-CaseBranch ((p , normal b q)  branches) = (p , normal (interp b) q)  interp-CaseBranch branches
  interp-CaseBranch ((p , adaptive u)  branches) = (p , adaptive u)  interp-CaseBranch branches