module Implicit.Language.EnvOps.InsertEVar where

open import Implicit.Language.Base
open import Implicit.Language.Shift.All
open import Implicit.Language.Lookup.All
open import Implicit.Language.Regular.Base
open import Implicit.Language.OpenClose.Base
open import Implicit.Language.Ground.Base
open import Implicit.Language.EnvOps.Base
open import Implicit.Language.EnvOps.Inst

infix 3 _▶_,^⇘_
data _▶_,^⇘_ : Env n m  Fin (1 + m)  Env n (1 + m)  Set where
  ▶Z : Γ  #0 ,^⇘ Γ ,^
  ▶S, : Γ  k ,^⇘ Γ'
       (upA : A ↑ty k  A')
       Γ , A  k ,^⇘ Γ' , A'
  ▶S^ : Γ  k ,^⇘ Γ'
       Γ ,^  #S k ,^⇘ Γ' ,^
  ▶S∙ : Γ  k ,^⇘ Γ'
       Γ ,∙  #S k ,^⇘ Γ' ,∙
  ▶S= : Γ  k ,^⇘ Γ'
       (upA : A ↑ty k  A')
       Γ ,= A  #S k ,^⇘ Γ' ,= A'
  ▶S⋈ : Γ  k ,^⇘ Γ'
       Γ   k ,^⇘ Γ' 


infix 3 _⨟_▶_,^⇘_⨟_
data _⨟_▶_,^⇘_⨟_ : Env n m  Env n m  Fin (1 + m)  Env n (1 + m)  Env n (1 + m)  Set where
  ▶Z : Γ  Δ  #0 ,^⇘ Γ ,^  Δ ,^
  ▶S, : Γ  Δ  k ,^⇘ Γ'  Δ'
       (upA : A ↑ty k  A')
       Γ , A  Δ , A  k ,^⇘ Γ' , A'  Δ' , A'
  ▶S^ : Γ  Δ  k ,^⇘ Γ'  Δ'
       Γ ,^  Δ ,^  #S k ,^⇘ Γ' ,^  Δ' ,^
  ▶S^= : Γ  Δ  k ,^⇘ Γ'  Δ'
        (upA : A ↑ty k  A')
       Γ ,^  Δ ,= A  #S k ,^⇘ Γ' ,^  Δ' ,= A'
  ▶S∙ : Γ  Δ  k ,^⇘ Γ'  Δ'
       Γ ,∙  Δ ,∙  #S k ,^⇘ Γ' ,∙  Δ' ,∙
  ▶S= : Γ  Δ  k ,^⇘ Γ'  Δ'
       (upA : A ↑ty k  A')
       Γ ,= A  Δ ,= A  #S k ,^⇘ Γ' ,= A'  Δ' ,= A'
  ▶S⋈ : Γ  Δ  k ,^⇘ Γ'  Δ'
       Γ   Δ   k ,^⇘ Γ'   Δ' 


▶^-▶⨟^ : Γ  k ,^⇘ Γ'
        Γ  Γ  k ,^⇘ Γ'  Γ'
▶^-▶⨟^ ▶Z = ▶Z
▶^-▶⨟^ (▶S, newΓ upA) = ▶S, (▶^-▶⨟^ newΓ) upA
▶^-▶⨟^ (▶S^ newΓ) = ▶S^ (▶^-▶⨟^ newΓ)
▶^-▶⨟^ (▶S∙ newΓ) = ▶S∙ (▶^-▶⨟^ newΓ)
▶^-▶⨟^ (▶S= newΓ upA) = ▶S= (▶^-▶⨟^ newΓ) upA
▶^-▶⨟^ (▶S⋈ newΓ) = ▶S⋈ (▶^-▶⨟^ newΓ)

▶⨟^-▶^-l : Γ  Δ  k ,^⇘ Γ'  Δ'
        Γ  k ,^⇘ Γ'
▶⨟^-▶^-l ▶Z = ▶Z
▶⨟^-▶^-l (▶S, new upA) = ▶S, (▶⨟^-▶^-l new) upA
▶⨟^-▶^-l (▶S^ new) = ▶S^ (▶⨟^-▶^-l new)
▶⨟^-▶^-l (▶S^= new upA) = ▶S^ (▶⨟^-▶^-l new)
▶⨟^-▶^-l (▶S∙ new) = ▶S∙ (▶⨟^-▶^-l new)
▶⨟^-▶^-l (▶S= new upA) = ▶S= (▶⨟^-▶^-l new) upA
▶⨟^-▶^-l (▶S⋈ new) = ▶S⋈ (▶⨟^-▶^-l new)

▶⨟^-unique : Γ  Γ  k ,^⇘ Γ'  Δ'
           Γ'  Δ'
▶⨟^-unique ▶Z = refl
▶⨟^-unique (▶S, ss upA) with refl▶⨟^-unique ss = refl
▶⨟^-unique (▶S^ ss) with refl▶⨟^-unique ss = refl
▶⨟^-unique (▶S∙ ss) with refl▶⨟^-unique ss = refl
▶⨟^-unique (▶S= ss upA) with refl▶⨟^-unique ss = refl
▶⨟^-unique (▶S⋈ ss) with refl▶⨟^-unique ss = refl


∋⦂-weaken^ : Γ  x  A
            Γ  k ,^⇘ Γ'
            A ↑ty k  A'
            Γ'  x  A'
∋⦂-weaken^ Z ▶Z upA = S^ Z upA
∋⦂-weaken^ Z (▶S, newΓ upA₁) upA
  with refl↑ty-unique upA upA₁ = Z
∋⦂-weaken^ (S, inΓ) ▶Z upA = S^ (S, inΓ) upA
∋⦂-weaken^ (S, inΓ) (▶S, newΓ upA₁) upA = S, (∋⦂-weaken^ inΓ newΓ upA)
∋⦂-weaken^ (S∙ inΓ up) ▶Z upA = S^ (S∙ inΓ up) upA
∋⦂-weaken^ {k = #S k} (S∙ {A = A} inΓ up) (▶S∙ newΓ) upA
  with  A' , upA' ↑ty-total A k = S∙ (∋⦂-weaken^ inΓ newΓ upA') (↑ty-comm0 up upA upA')
∋⦂-weaken^ (S^ inΓ up) ▶Z upA = S^ (S^ inΓ up) upA
∋⦂-weaken^ {k = #S k} (S^ {A = A} inΓ up) (▶S^ newΓ) upA
  with  A' , upA' ↑ty-total A k = S^ (∋⦂-weaken^ inΓ newΓ upA') (↑ty-comm0 up upA upA')
∋⦂-weaken^ (S= inΓ up) ▶Z upA = S^ (S= inΓ up) upA
∋⦂-weaken^ {k = #S k} (S= {A = A} inΓ up) (▶S= newΓ upA₁) upA
  with  A' , upA' ↑ty-total A k = S= (∋⦂-weaken^ inΓ newΓ upA') (↑ty-comm0 up upA upA')

∋=-weaken^ : Γ ∋= X
            Γ  k ,^⇘ Γ'
            Γ' ∋= punchIn k X
∋=-weaken^ Z ▶Z = S^ Z
∋=-weaken^ Z (▶S= newΓ upA) = Z
∋=-weaken^ (S∙ inΓ) ▶Z = S^ (S∙ inΓ)
∋=-weaken^ (S∙ inΓ) (▶S∙ newΓ) = S∙ (∋=-weaken^ inΓ newΓ)
∋=-weaken^ (S^ inΓ) ▶Z = S^ (S^ inΓ)
∋=-weaken^ (S^ inΓ) (▶S^ newΓ) = S^ (∋=-weaken^ inΓ newΓ)
∋=-weaken^ (S= inΓ) ▶Z = S^ (S= inΓ)
∋=-weaken^ (S= inΓ) (▶S= newΓ upA) = S= (∋=-weaken^ inΓ newΓ)
∋=-weaken^ (S, inΓ) ▶Z = S^ (S, inΓ)
∋=-weaken^ (S, inΓ) (▶S, new upA) = S, (∋=-weaken^ inΓ new)

∋^-weaken^ : Γ ∋^ X
            Γ  k ,^⇘ Γ'
            Γ' ∋^ punchIn k X
∋^-weaken^ Z ▶Z = S^ Z
∋^-weaken^ Z (▶S^ newΓ) = Z
∋^-weaken^ (S∙ inΓ) ▶Z = S^ (S∙ inΓ)
∋^-weaken^ (S∙ inΓ) (▶S∙ newΓ) = S∙ (∋^-weaken^ inΓ newΓ)
∋^-weaken^ (S^ inΓ) ▶Z = S^ (S^ inΓ)
∋^-weaken^ (S^ inΓ) (▶S^ newΓ) = S^ (∋^-weaken^ inΓ newΓ)
∋^-weaken^ (S= inΓ) ▶Z = S^ (S= inΓ)
∋^-weaken^ (S= inΓ) (▶S= newΓ upA) = S= (∋^-weaken^ inΓ newΓ)
∋^-weaken^ (S, inΓ) ▶Z = S^ (S, inΓ)
∋^-weaken^ (S, inΓ) (▶S, new upA) = S, (∋^-weaken^ inΓ new)


∋∙-weaken^ : Γ ∋∙ X
            Γ  k ,^⇘ Γ'
            Γ' ∋∙ punchIn k X
∋∙-weaken^ Z ▶Z = S^ Z
∋∙-weaken^ Z (▶S∙ newΓ) = Z
∋∙-weaken^ (S, inΓ) ▶Z = S^ (S, inΓ)
∋∙-weaken^ (S, inΓ) (▶S, newΓ upA) = S, (∋∙-weaken^ inΓ newΓ)
∋∙-weaken^ (S∙ inΓ) ▶Z = S^ (S∙ inΓ)
∋∙-weaken^ (S∙ inΓ) (▶S∙ newΓ) = S∙ (∋∙-weaken^ inΓ newΓ)
∋∙-weaken^ (S= inΓ) ▶Z = S^ (S= inΓ)
∋∙-weaken^ (S= inΓ) (▶S= newΓ upA) = S= (∋∙-weaken^ inΓ newΓ)
∋∙-weaken^ (S^ inΓ) ▶Z = S^ (S^ inΓ)
∋∙-weaken^ (S^ inΓ) (▶S^ newΓ) = S^ (∋∙-weaken^ inΓ newΓ)
∋∙-weaken^ (S⋈ inΓ) ▶Z = S^ (S⋈ inΓ)
∋∙-weaken^ (S⋈ inΓ) (▶S⋈ newΓ) = S⋈ (∋∙-weaken^ inΓ newΓ)

∋:=-weaken^ : Γ  X := A
             A ↑ty k  A'
             Γ  k ,^⇘ Γ'
             Γ'  punchIn k X := A'
∋:=-weaken^ (Z up) upA ▶Z = S^ (Z up) upA
∋:=-weaken^ (Z up) upA (▶S= newΓ upA₁) = Z (↑ty-comm0 up upA upA₁)
∋:=-weaken^ (S∙ inΓ up) upA ▶Z = S^ (S∙ inΓ up) upA
∋:=-weaken^ (S∙ {A = A} inΓ up) upA (▶S∙ {k = k} newΓ)
  with  A' , upA' ↑ty-total A k = S∙ (∋:=-weaken^ inΓ upA' newΓ) (↑ty-comm0 up upA upA')
∋:=-weaken^ (S^ inΓ up) upA ▶Z = S^ (S^ inΓ up) upA
∋:=-weaken^ (S^ {A = A} inΓ up) upA (▶S^ {k = k} newΓ)
  with  A' , upA' ↑ty-total A k = S^ (∋:=-weaken^ inΓ upA' newΓ) (↑ty-comm0 up upA upA')
∋:=-weaken^ (S= inΓ up) upA ▶Z = S^ (S= inΓ up) upA
∋:=-weaken^ (S= {A = A} inΓ up) upA (▶S= {k = k} newΓ upA₁)
  with  A' , upA' ↑ty-total A k = S= (∋:=-weaken^ inΓ upA' newΓ) (↑ty-comm0 up upA upA')
∋:=-weaken^ (S, inΓ) upA ▶Z = S^ (S, inΓ) upA
∋:=-weaken^ (S, inΓ) upA (▶S, new upA₁) = S, (∋:=-weaken^ inΓ upA new)

⊢r-weaken^ : Γ ⊢r A
             Γ  k ,^⇘ Γ'
             A ↑ty k  A'
             Γ' ⊢r A'
⊢r-weaken^ ⊢r-int newΓ ↑ty-int = ⊢r-int
⊢r-weaken^ (⊢r-var-∙ inΓ) newΓ ↑ty-var = ⊢r-var-∙ (∋∙-weaken^ inΓ newΓ)
⊢r-weaken^ (⊢r-arr regA regA₁) newΓ (↑ty-arr upA upA₁) = ⊢r-arr (⊢r-weaken^ regA newΓ upA) (⊢r-weaken^ regA₁ newΓ upA₁)
⊢r-weaken^ (⊢r-∀ regA) newΓ (↑ty-∀ upA) = ⊢r-∀ (⊢r-weaken^ regA (▶S∙ newΓ) upA)

⊢c-weaken^ : Γ ⊢c A
             Γ  k ,^⇘ Γ'
             A ↑ty k  A'
             Γ' ⊢c A'
⊢c-weaken^ ⊢c-int newΓ ↑ty-int = ⊢c-int
⊢c-weaken^ (⊢c-var-∙ inΔ) newΓ ↑ty-var = ⊢c-var-∙ (∋∙-weaken^ inΔ newΓ)
⊢c-weaken^ (⊢c-var-= inΔ) newΓ ↑ty-var = ⊢c-var-= (∋=-weaken^ inΔ newΓ)
⊢c-weaken^ (⊢c-arr cloA cloA₁) newΓ (↑ty-arr upA upA₁) = ⊢c-arr (⊢c-weaken^ cloA newΓ upA) (⊢c-weaken^ cloA₁ newΓ upA₁)
⊢c-weaken^ (⊢c-∀ cloA) newΓ (↑ty-∀ upA) = ⊢c-∀ (⊢c-weaken^ cloA (▶S∙ newΓ) upA)

⊢o-weaken^ : Γ ⊢o A
             Γ  k ,^⇘ Γ'
             A ↑ty k  A'
             Γ' ⊢o A'
⊢o-weaken^ (⊢o-var-^ x) new ↑ty-var = ⊢o-var-^ (∋^-weaken^ x new)
⊢o-weaken^ (⊢o-arr-l opnA) new (↑ty-arr upA upA₁) = ⊢o-arr-l (⊢o-weaken^ opnA new upA)
⊢o-weaken^ (⊢o-arr-r opnA) new (↑ty-arr upA upA₁) = ⊢o-arr-r (⊢o-weaken^ opnA new upA₁)
⊢o-weaken^ (⊢o-∀ opnA) new (↑ty-∀ upA) = ⊢o-∀ (⊢o-weaken^ opnA (▶S∙ new) upA)

≫-weaken^ : Γ  A  B
           Γ  k ,^⇘ Γ'
           A ↑ty k  A'
           B ↑ty k  B'
           Γ'  A'  B'
≫-weaken^ grd-int newΓ ↑ty-int ↑ty-int = grd-int
≫-weaken^ (grd-var= x) newΓ ↑ty-var upB = grd-var= (∋:=-weaken^ x upB newΓ)
≫-weaken^ (grd-var∙ x) newΓ ↑ty-var ↑ty-var = grd-var∙ (∋∙-weaken^ x newΓ)
≫-weaken^ (grd-arr grd grd₁) newΓ (↑ty-arr upA upA₁) (↑ty-arr upB upB₁) = grd-arr (≫-weaken^ grd newΓ upA upB)
                                                                                  (≫-weaken^ grd₁ newΓ upA₁ upB₁)
≫-weaken^ (grd-∀ grd) newΓ (↑ty-∀ upA) (↑ty-∀ upB) = grd-∀ (≫-weaken^ grd (▶S∙ newΓ) upA upB)

tregular-weaken^ : TRegular Γ
                  Γ  k ,^⇘ Γ'
                  TRegular Γ'
tregular-weaken^ reg-Z ▶Z = reg-S^ reg-Z
tregular-weaken^ (reg-S, regΓ regA) ▶Z = reg-S^ (reg-S, regΓ regA)
tregular-weaken^ (reg-S, regΓ regA) (▶S, newΓ upA) = reg-S, (tregular-weaken^ regΓ newΓ) (⊢r-weaken^ regA newΓ upA)
tregular-weaken^ (reg-S∙ regΓ) ▶Z = reg-S^ (reg-S∙ regΓ)
tregular-weaken^ (reg-S∙ regΓ) (▶S∙ newΓ) = reg-S∙ (tregular-weaken^ regΓ newΓ)
tregular-weaken^ (reg-S^ regΓ) ▶Z = reg-S^ (reg-S^ regΓ)
tregular-weaken^ (reg-S^ regΓ) (▶S^ newΓ) = reg-S^ (tregular-weaken^ regΓ newΓ)
tregular-weaken^ (reg-S= regΓ regA) ▶Z = reg-S^ (reg-S= regΓ regA)
tregular-weaken^ (reg-S= regΓ regA) (▶S= newΓ upA) = reg-S= (tregular-weaken^ regΓ newΓ) (⊢r-weaken^ regA newΓ upA)

sregular-weaken^ : SRegular Γ
                  Γ  k ,^⇘ Γ'
                  SRegular Γ'
sregular-weaken^ (reg-Z regΓ) ▶Z = reg-S^ (reg-Z regΓ)
sregular-weaken^ (reg-Z regΓ) (▶S⋈ newΓ) = reg-Z (tregular-weaken^ regΓ newΓ)
sregular-weaken^ (reg-S∙ regΓ) ▶Z = reg-S^ (reg-S∙ regΓ)
sregular-weaken^ (reg-S∙ regΓ) (▶S∙ newΓ) = reg-S∙ (sregular-weaken^ regΓ newΓ)
sregular-weaken^ (reg-S^ regΓ) ▶Z = reg-S^ (reg-S^ regΓ)
sregular-weaken^ (reg-S^ regΓ) (▶S^ newΓ) = reg-S^ (sregular-weaken^ regΓ newΓ)
sregular-weaken^ (reg-S= regΓ regA) ▶Z = reg-S^ (reg-S= regΓ regA)
sregular-weaken^ (reg-S= regΓ regA) (▶S= newΓ upA) = reg-S= (sregular-weaken^ regΓ newΓ) (⊢r-weaken^ regA newΓ upA)


inst-weaken^ : [ A / X ] Γ  Δ
              Γ  Δ  k ,^⇘ Γ'  Δ'
              A ↑ty k  A'
              [ A' / punchIn k X ] Γ'  Δ'
inst-weaken^ (⟹^0 up regA env) ▶Z upA = ⟹^S (⟹^0 up regA env) upA
inst-weaken^ (⟹^0 up regA env) (▶S^= new upA₁) upA
  with refl▶⨟^-unique new = ⟹^0 (↑ty-comm0 up upA upA₁) (⊢r-weaken^ regA (▶⨟^-▶^-l new) upA₁) (sregular-weaken^ env (▶⨟^-▶^-l new))
inst-weaken^ (⟹^S inst up1) ▶Z upA = ⟹^S (⟹^S inst up1) upA
inst-weaken^ {k = #S k} (⟹^S {A = A} inst up1) (▶S^ new) upA
  with  A' , upA' ↑ty-total A k = ⟹^S (inst-weaken^ inst new upA') (↑ty-comm0 up1 upA upA')
inst-weaken^ (⟹∙S inst up1) ▶Z upA = ⟹^S (⟹∙S inst up1) upA
inst-weaken^ {k = #S k} (⟹∙S {A = A} inst up1) (▶S∙ new) upA
  with  A' , upA' ↑ty-total A k = ⟹∙S (inst-weaken^ inst new upA') (↑ty-comm0 up1 upA upA')
inst-weaken^ (⟹=S inst up1 regB) ▶Z upA = ⟹^S (⟹=S inst up1 regB) upA
inst-weaken^ {k = #S k} (⟹=S {A = A} inst up1 regB) (▶S= new upA₁) upA
   with  A' , upA' ↑ty-total A k = ⟹=S (inst-weaken^ inst new upA') (↑ty-comm0 up1 upA upA') (⊢r-weaken^ regB (▶⨟^-▶^-l new) upA₁)