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

module STLC.NbE where

open import Lib
open import STLC.Statics
open import Data.Unit using (tt; )

data Nf : Ctx  Typ  Set
data Ne : Ctx  Typ  Set

data Nf where
  *  : Nf Γ *
  pr : (s : Nf Γ S) (u : Nf Γ U)  Nf Γ (S X U)
  Λ  : Nf (S  Γ) U  Nf Γ (S  U)
  ne : (t : Ne Γ T)  Nf Γ T

data Ne where
  var : T  Γ  Ne Γ T
  π₁  : Ne Γ (S X U)  Ne Γ S
  π₂  : Ne Γ (S X U)  Ne Γ U
  _$_ : Ne Γ (S  U)  (s : Nf Γ S)  Ne Γ U

Nf⇒Trm : Nf Γ T  Trm Γ T
Ne⇒Trm : Ne Γ T  Trm Γ T

Nf⇒Trm *        = *
Nf⇒Trm (pr s u) = pr (Nf⇒Trm s) (Nf⇒Trm u)
Nf⇒Trm (Λ t)    = Λ (Nf⇒Trm t)
Nf⇒Trm (ne t)   = Ne⇒Trm t

Ne⇒Trm (var T∈Γ) = var T∈Γ
Ne⇒Trm (π₁ t)    = π₁ (Ne⇒Trm t)
Ne⇒Trm (π₂ t)    = π₂ (Ne⇒Trm t)
Ne⇒Trm (t $ s)   = Ne⇒Trm t $ Nf⇒Trm s

shiftₙ : Nf Γ T  Γ  Γ′  Nf Γ′ T
shiftₑ : Ne Γ T  Γ  Γ′  Ne Γ′ T

shiftₙ * Γ⊆Γ′        = *
shiftₙ (pr s u) Γ⊆Γ′ = pr (shiftₙ s Γ⊆Γ′) (shiftₙ u Γ⊆Γ′)
shiftₙ (Λ t) Γ⊆Γ′    = Λ (shiftₙ t (refl  Γ⊆Γ′))
shiftₙ (ne t) Γ⊆Γ′   = ne (shiftₑ t Γ⊆Γ′)

shiftₑ (var T∈Γ) Γ⊆Γ′ = var (∈-⊆ T∈Γ Γ⊆Γ′)
shiftₑ (π₁ t) Γ⊆Γ′    = π₁ (shiftₑ t Γ⊆Γ′)
shiftₑ (π₂ t) Γ⊆Γ′    = π₂ (shiftₑ t Γ⊆Γ′)
shiftₑ (t $ s) Γ⊆Γ′   = shiftₑ t Γ⊆Γ′ $ shiftₙ s Γ⊆Γ′

shiftₙ-shiftₙ :  (t : Nf Γ T) (Γ⊆Γ′ : Γ  Γ′) (Γ′⊆Γ″ : Γ′  Γ″) 
  shiftₙ (shiftₙ t Γ⊆Γ′) Γ′⊆Γ″  shiftₙ t (⊆-trans Γ⊆Γ′ Γ′⊆Γ″)

shiftₑ-shiftₑ :  (t : Ne Γ T) (Γ⊆Γ′ : Γ  Γ′) (Γ′⊆Γ″ : Γ′  Γ″) 
  shiftₑ (shiftₑ t Γ⊆Γ′) Γ′⊆Γ″  shiftₑ t (⊆-trans Γ⊆Γ′ Γ′⊆Γ″)

shiftₙ-shiftₙ * Γ⊆Γ′ Γ′⊆Γ″        = refl
shiftₙ-shiftₙ (pr s u) Γ⊆Γ′ Γ′⊆Γ″ = cong₂ pr (shiftₙ-shiftₙ s Γ⊆Γ′ Γ′⊆Γ″) (shiftₙ-shiftₙ u Γ⊆Γ′ Γ′⊆Γ″)
shiftₙ-shiftₙ (Λ t) Γ⊆Γ′ Γ′⊆Γ″    = cong Λ (shiftₙ-shiftₙ t (refl  Γ⊆Γ′) (refl  Γ′⊆Γ″))
shiftₙ-shiftₙ (ne t) Γ⊆Γ′ Γ′⊆Γ″   = cong ne (shiftₑ-shiftₑ t Γ⊆Γ′ Γ′⊆Γ″)

shiftₑ-shiftₑ (var T∈Γ) Γ⊆Γ′ Γ′⊆Γ″ = cong var (∈-⊆-trans T∈Γ Γ⊆Γ′ Γ′⊆Γ″)
shiftₑ-shiftₑ (π₁ t) Γ⊆Γ′ Γ′⊆Γ″    = cong π₁ (shiftₑ-shiftₑ t Γ⊆Γ′ Γ′⊆Γ″)
shiftₑ-shiftₑ (π₂ t) Γ⊆Γ′ Γ′⊆Γ″    = cong π₂ (shiftₑ-shiftₑ t Γ⊆Γ′ Γ′⊆Γ″)
shiftₑ-shiftₑ (t $ s) Γ⊆Γ′ Γ′⊆Γ″   = cong₂ _$_ (shiftₑ-shiftₑ t Γ⊆Γ′ Γ′⊆Γ″) (shiftₙ-shiftₙ s Γ⊆Γ′ Γ′⊆Γ″)


⟦_⊢_⟧ : Ctx  Typ  Set
 Γ  *      = 
 Γ  S X U  =  Γ  S  ×  Γ  U 
 Γ  S  U  =  {Γ′}  Γ  Γ′   Γ′  S    Γ′  U 

reify :  Γ  T   Nf Γ T
reflect : Ne Γ T   Γ  T 

reify {_} {*} sem           = *
reify {_} {S X U} (s₁ , s₂) = pr (reify s₁) (reify s₂)
reify {_} {S  U} sem       = Λ (reify (sem (S ∷ʳ ⊆-refl) (reflect (var 0d))))

reflect {_} {*} t              = tt
reflect {_} {S X U} t          = reflect (π₁ t) , reflect (π₂ t)
reflect {_} {S  U} t Γ⊆Γ′ ⟦S⟧ = reflect (shiftₑ t Γ⊆Γ′ $ reify ⟦S⟧)

shiftₛ : Γ  Γ′   Γ  T    Γ′  T 
shiftₛ {_} {_} {*} Γ⊆Γ′ ⟦T⟧             = tt
shiftₛ {_} {_} {S X U} Γ⊆Γ′ (⟦S⟧ , ⟦U⟧) = shiftₛ Γ⊆Γ′ ⟦S⟧ , shiftₛ Γ⊆Γ′ ⟦U⟧
shiftₛ {_} {_} {S  U} Γ⊆Γ′ ⟦T⟧ Γ′⊆Γ″   = ⟦T⟧ (⊆-trans Γ⊆Γ′ Γ′⊆Γ″)

⟦_⇒_⟧ : Ctx  Ctx  Set
⟦_⇒_⟧ Γ = All  Γ ⊢_⟧

eval :  Δ  Γ   Trm Γ T   Δ  T 
eval ΔΓ *              = tt
eval ΔΓ (var T∈Γ)      = All′.lookup ΔΓ T∈Γ
eval ΔΓ (pr s u)       = eval ΔΓ s , eval ΔΓ u
eval ΔΓ (π₁ t)         = proj₁ (eval ΔΓ t)
eval ΔΓ (π₂ t)         = proj₂ (eval ΔΓ t)
eval ΔΓ (s $ u)        = eval ΔΓ s ⊆-refl (eval ΔΓ u)
eval ΔΓ (Λ t) Δ⊆Γ′ ⟦S⟧ = eval (⟦S⟧  All′.map (shiftₛ Δ⊆Γ′) ΔΓ) t

⇒-id :  Γ  Γ 
⇒-id {[]}    = []
⇒-id {T  Γ} = reflect (var 0d)  All′.map (shiftₛ (T ∷ʳ ⊆-refl)) ⇒-id

nbe : Trm Γ T  Nf Γ T
nbe t = reify (eval ⇒-id t)