module Implicit.Algo.Properties.WeakenEVar where

open import Implicit.Language.All
open import Implicit.Algo.Base
open import Implicit.Algo.Properties.Extension
open import Implicit.Algo.Properties.Shift
open import Implicit.Algo.Properties.Id

▶⨟^-Ω-exist : Γ  Δ  k ,^⇘ Γ'  Δ'
             Γ  Ω
             Ω  Δ
             ∃[ Ω' ](Γ  Ω  k ,^⇘ Γ'  Ω' ×
               Ω  Δ  k ,^⇘ Ω'  Δ')
▶⨟^-Ω-exist {Ω = Ω} ▶Z ext1 ext2 =  Ω ,^ ,  ▶Z , ▶Z  
▶⨟^-Ω-exist (▶S^ new) (evar ext1) (evar ext2) =  ▶⨟^-Ω-exist new ext1 ext2 .proj₁ ,^ ,
                                                  ▶S^ (▶⨟^-Ω-exist new ext1 ext2 .proj₂ .proj₁) ,
                                                 ▶S^ (▶⨟^-Ω-exist new ext1 ext2 .proj₂ .proj₂)  
▶⨟^-Ω-exist (▶S^= new upA) (evar ext1) (evar-sol ext2 regA) =  ▶⨟^-Ω-exist new ext1 ext2 .proj₁ ,^ ,
                                                                ▶S^ (▶⨟^-Ω-exist new ext1 ext2 .proj₂ .proj₁) ,
                                                               ▶S^= (▶⨟^-Ω-exist new ext1 ext2 .proj₂ .proj₂) upA  
▶⨟^-Ω-exist (▶S^= {A' = A'} new upA) (evar-sol ext1 regA) (svar ext2 regA₁) =  ▶⨟^-Ω-exist new ext1 ext2 .proj₁ ,= A' ,
                                                                      ▶S^= (▶⨟^-Ω-exist new ext1 ext2 .proj₂ .proj₁) upA ,
                                                                     ▶S= (▶⨟^-Ω-exist new ext1 ext2 .proj₂ .proj₂) upA  
▶⨟^-Ω-exist (▶S∙ new) (uvar ext1) (uvar ext2) =  ▶⨟^-Ω-exist new ext1 ext2 .proj₁ ,∙ ,
                                                  ▶S∙ (▶⨟^-Ω-exist new ext1 ext2 .proj₂ .proj₁) ,
                                                 ▶S∙ (▶⨟^-Ω-exist new ext1 ext2 .proj₂ .proj₂)  
▶⨟^-Ω-exist (▶S= {A' = A'} new upA) (svar ext1 regA) (svar ext2 regA₁) =  ▶⨟^-Ω-exist new ext1 ext2 .proj₁ ,= A' ,
                                                                 ▶S= (▶⨟^-Ω-exist new ext1 ext2 .proj₂ .proj₁) upA ,
                                                                ▶S= (▶⨟^-Ω-exist new ext1 ext2 .proj₂ .proj₂) upA  
▶⨟^-Ω-exist (▶S⋈ {Γ' = Γ'} new) (mark regΓ) (mark regΓ₁)
  with refl▶⨟^-unique new =  Γ'  ,  ▶S⋈ new , ▶S⋈ new  

ss-weaken^ : Γ  A    B  Δ
            Γ  Δ  k ,^⇘ Γ'  Δ'
            A ↑ty k  A'
            B ↑ty k  B'
            Γ'  A'    B'  Δ'
ss-weaken^ (s-int regΓ) new ↑ty-int ↑ty-int
  with refl▶⨟^-unique new = s-int (sregular-weaken^ regΓ (▶⨟^-▶^-l new))
ss-weaken^ (s-var-∙ regΓ x) new ↑ty-var ↑ty-var
  with refl▶⨟^-unique new = s-var-∙ (sregular-weaken^ regΓ (▶⨟^-▶^-l new)) (∋∙-weaken^ x (▶⨟^-▶^-l new))
ss-weaken^ (s-ex-l^ inst) new ↑ty-var upB = s-ex-l^ (inst-weaken^ inst new upB)
ss-weaken^ (s-ex-r^ inst) new upA ↑ty-var = s-ex-r^ (inst-weaken^ inst new upA)
ss-weaken^ (s-ex-l= regΓ x-in) new ↑ty-var upB
  with refl▶⨟^-unique new = s-ex-l= (sregular-weaken^ regΓ (▶⨟^-▶^-l new)) (∋:=-weaken^ x-in upB (▶⨟^-▶^-l new))
ss-weaken^ (s-ex-r= regΓ x-in) new upA ↑ty-var
  with refl▶⨟^-unique new = s-ex-r= (sregular-weaken^ regΓ (▶⨟^-▶^-l new)) (∋:=-weaken^ x-in upA (▶⨟^-▶^-l new))
ss-weaken^ (s-arr ss ss₁) new (↑ty-arr upA upA₁) (↑ty-arr upB upB₁)
  with  Ω' ,  new1 , new2  ▶⨟^-Ω-exist new (ss-⊆ ss) (ss-⊆ ss₁)
  = s-arr (ss-weaken^ ss new1 upB upA) (ss-weaken^ ss₁ new2 upA₁ upB₁)
ss-weaken^ (s-∀ ss) new (↑ty-∀ upA) (↑ty-∀ upB) = s-∀ (ss-weaken^ ss (▶S∙ new) upA upB)

▶^-𝕣 : Γ  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) = new


t-weaken^ : Γ  Σ  e  A
           Γ  k ,^⇘ Γ'
           Σ ↑tyᶜ k  Σ'
           e ↑tyᵉ k  e'
           A ↑ty k  A'
           Γ'  Σ'  e'  A'

infs-weaken^ : Γ  Σ  A
              Γ  k ,^⇘ Γ'
              Σ ↑tyᶜ k  Σ'
              A ↑ty k  A'
              Γ'  Σ'  A'
infs-weaken^ (infs-z regΓ regA) newΓ (↑tyᶜ-τ up-t) upA
  with refl↑ty-unique up-t upA = infs-z (tregular-weaken^ regΓ newΓ) (⊢r-weaken^ regA newΓ upA)
infs-weaken^ (infs-s ⊢e infs) newΓ (↑tyᶜ-e up-e upΣ) (↑ty-arr upA upA₁) = infs-s (t-weaken^ ⊢e newΓ ↑tyᶜ-□ up-e upA) (infs-weaken^ infs newΓ upΣ upA₁)

s-weaken^ : Γ  A ≤⁺ Σ  Δ  B
           Γ  Δ  k ,^⇘ Γ'  Δ'
           A ↑ty k  A'
           Σ ↑tyᶜ k  Σ'
           B ↑ty k  B'
           Γ'  A' ≤⁺ Σ'  Δ'  B'
s-weaken^ (s-empty regΓ cloA x) new upA ↑tyᶜ-□ upB
  with refl▶⨟^-unique new = s-empty (sregular-weaken^ regΓ (▶⨟^-▶^-l new)) (⊢c-weaken^ cloA (▶⨟^-▶^-l new) upA) (≫-weaken^ x (▶⨟^-▶^-l new) upA upB)
s-weaken^ (s-type ss) new upA (↑tyᶜ-τ up-t) upB
  with refl↑ty-unique up-t upB = s-type (ss-weaken^ ss new upA up-t)
s-weaken^ (s-term-c cloA ap ⊢e s) new (↑ty-arr upA upA₁) (↑tyᶜ-e up-e upΣ) (↑ty-arr upB upB₁)
  with refl⊢id0 ⊢e
  = s-term-c (⊢c-weaken^ cloA (▶⨟^-▶^-l new) upA) (≫-weaken^ ap (▶⨟^-▶^-l new) upA upB) (t-weaken^ ⊢e (▶^-𝕣 (▶⨟^-▶^-l new)) (↑tyᶜ-τ upB) up-e upB)
             (s-weaken^ s new upA₁ upΣ upB₁)
s-weaken^ (s-term-o opnA ⊢e ss s) new (↑ty-arr upA upA₁) (↑tyᶜ-e up-e upΣ) (↑ty-arr upB upB₁)
  with  Ω' ,  new1 , new2  ▶⨟^-Ω-exist new (ss-⊆ ss) (s-⊆ s)
  = s-term-o (⊢o-weaken^ opnA (▶⨟^-▶^-l new) upA)
             (t-weaken^ ⊢e (▶^-𝕣 (▶⨟^-▶^-l new)) ↑tyᶜ-□ up-e upB)
             (ss-weaken^ ss new1 upB upA)
             (s-weaken^ s new2 upA₁ upΣ upB₁)
s-weaken^ {k = k} (s-∀l {B = B} s upᶜ upᵉ upC upD) new (↑ty-∀ upA) (↑tyᶜ-e {e' = e'} {Σ' = Σ'} up-e upΣ) (↑ty-arr {A' = C'} {B' = D'} upC' upD')
  with  B' , upB' ↑ty-total B k
  with  C″ , upC″ ↑ty0-total C'
  with  D″ , upD″ ↑ty0-total D'
  with  e″ , upe″ ↑tyᵉ0-total e'
  with  Σ″ , upΣ″ ↑tyᶜ0-total Σ'
  = s-∀l (s-weaken^ s (▶S^= new upB') upA
         (↑tyᶜ-comm0' (↑tyᶜ-e up-e upΣ) (↑tyᶜ-e upe″ upΣ″) (↑tyᶜ-e upᵉ upᶜ))
         (↑ty-arr (↑ty-comm0' upC' upC″ upC) (↑ty-comm0' upD' upD″ upD)))
         upΣ″ upe″ upC″ upD″
s-weaken^ {k = k} (s-∀l-no s upᶜ upᵉ upC upD) new (↑ty-∀ upA) (↑tyᶜ-e {e' = e'} {Σ' = Σ'} up-e upΣ) (↑ty-arr {A' = C'} {B' = D'} upC' upD')
  with  C″ , upC″ ↑ty0-total C'
  with  D″ , upD″ ↑ty0-total D'
  with  e″ , upe″ ↑tyᵉ0-total e'
  with  Σ″ , upΣ″ ↑tyᶜ0-total Σ'
  = s-∀l-no (s-weaken^ s (▶S^ new) upA
         (↑tyᶜ-comm0' (↑tyᶜ-e up-e upΣ) (↑tyᶜ-e upe″ upΣ″) (↑tyᶜ-e upᵉ upᶜ))
         (↑ty-arr (↑ty-comm0' upC' upC″ upC) (↑ty-comm0' upD' upD″ upD)))
         upΣ″ upe″ upC″ upD″
s-weaken^ {k = k} (s-svar-term {A = A} inΓ s) new ↑ty-var (↑tyᶜ-e up-e upΣ) (↑ty-arr upB upB₁)
  with  A' , upA ↑ty-total A k
  with refl▶⨟^-unique new = s-svar-term (∋:=-weaken^ inΓ upA (▶⨟^-▶^-l new)) (s-weaken^ s new upA (↑tyᶜ-e up-e upΣ) (↑ty-arr upB upB₁))
s-weaken^ (s-evar-infers infs inst) new ↑ty-var (↑tyᶜ-e up-e upΣ) upB
  = s-evar-infers (infs-weaken^ infs (▶^-𝕣 (▶⨟^-▶^-l new)) (↑tyᶜ-e up-e upΣ) upB) (inst-weaken^ inst new upB)

t-weaken^ (⊢lit regΓ) newΓ ↑tyᶜ-□ ↑tyᵉ-lit ↑ty-int = ⊢lit (tregular-weaken^ regΓ newΓ)
t-weaken^ (⊢var regΓ x∈Γ) newΓ ↑tyᶜ-□ ↑tyᵉ-var upA = ⊢var (tregular-weaken^ regΓ newΓ) (∋⦂-weaken^ x∈Γ newΓ upA)
t-weaken^ {k = k} (⊢ann {B = B} ⊢e) newΓ ↑tyᶜ-□ (↑tyᵉ-⦂ upe up) upA
  with refl↑ty-unique up upA
  with  B' , upB ↑ty-total B k
  = ⊢ann (t-weaken^ ⊢e newΓ (↑tyᶜ-τ up) upe upB)
t-weaken^ {k = k} (⊢app {A = A} ⊢e) newΓ upΣ (↑tyᵉ-app upe upe₁) upA
  with  A' , upA' ↑ty-total A k
  = ⊢app (t-weaken^ ⊢e newΓ (↑tyᶜ-e upe₁ upΣ) upe (↑ty-arr upA' upA))
t-weaken^ (⊢lam₁ ⊢e) newΓ (↑tyᶜ-τ (↑ty-arr up-t up-t₁)) (↑tyᵉ-ƛ upe) (↑ty-arr upA upA₁)
  with refl↑ty-unique up-t upA = ⊢lam₁ (t-weaken^ ⊢e (▶S, newΓ up-t) (↑tyᶜ-τ up-t₁) upe upA₁)
t-weaken^ (⊢lam₂ ⊢e up-c ⊢e₁) newΓ (↑tyᶜ-e {Σ' = Σ'} up-e upΣ) (↑tyᵉ-ƛ upe) (↑ty-arr upA upA₁)
  with  Σ″ , upΣ' ↑tmᶜ0-total Σ'
  = ⊢lam₂ (t-weaken^ ⊢e newΓ ↑tyᶜ-□ up-e upA) upΣ' (t-weaken^ ⊢e₁ (▶S, newΓ upA) (↑tmᶜ-↑tyᶜ-comm' up-c upΣ upΣ') upe upA₁)
t-weaken^ {k = k} (⊢sub {A = A} ⊢e ne gc s) newΓ upΣ upe upA
  with  A' , upA' ↑ty-total A k
  = ⊢sub (t-weaken^ ⊢e newΓ ↑tyᶜ-□ upe upA')
         (nonempty-↑tyᶜ' ne upΣ) (gc-↑tyᵉ gc upe) (s-weaken^ s (▶S⋈ (▶^-▶⨟^ newΓ)) upA' upΣ upA)
t-weaken^ (⊢tabs ⊢e) newΓ ↑tyᶜ-□ (↑tyᵉ-Λ upe) (↑ty-∀ upA) = ⊢tabs (t-weaken^ ⊢e (▶S∙ newΓ) ↑tyᶜ-□ upe upA)
t-weaken^ (⊢tabs-τ ⊢e) newΓ (↑tyᶜ-τ (↑ty-∀ upA')) (↑tyᵉ-Λ upe) (↑ty-∀ upA) = ⊢tabs-τ (t-weaken^ ⊢e (▶S∙ newΓ) (↑tyᶜ-τ upA') upe upA)
t-weaken^ {k = k} (⊢tapp {B = B} {B* = B*} ⊢e st regA s) newΓ upΣ (↑tyᵉ-⓪ upe upA₁) upA
  with  B' , upB ↑ty-total B (#S k)
  with  B*' , upB* ↑ty-total B* k
  = ⊢tapp (t-weaken^ ⊢e newΓ ↑tyᶜ-□ upe (↑ty-∀ upB))
          (↑ty-st-comm0 st upA₁ upB upB*) (⊢r-weaken^ regA newΓ upA₁) (s-weaken^ s (▶^-▶⨟^ (▶S⋈ newΓ)) upB* upΣ upA)

t-weaken^0 : Γ  Σ  e  A
            ↑tyᶜ0 Σ  Σ'
            ↑tyᵉ0 e  e'
            ↑ty0 A  A'
            Γ ,^  Σ'  e'  A'
t-weaken^0 ⊢e upΣ upe upA = t-weaken^ ⊢e ▶Z upΣ upe upA

s-weaken^0 : Γ  A ≤⁺ Σ  Δ  B
            ↑ty0 A  A'
            ↑tyᶜ0 Σ  Σ'
            ↑ty0 B  B'
            Γ ,^  A' ≤⁺ Σ'  Δ ,^  B'
s-weaken^0 s upA upΣ upB = s-weaken^ s ▶Z upA upΣ upB