module Implicit.Algo.Properties.StrengthenEVar 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
open import Implicit.Algo.Properties.Regularity

abstract
  ◀^-unique : Γ  k ^⇘ Γ'
             Γ  k ^⇘ Δ'
             Γ'  Δ'
  ◀^-unique ◀Z ◀Z = refl
  ◀^-unique (◀S, new1 x) (◀S, new2 x₁) with refl◀^-unique new1 new2
                                       with refl↑ty-unique-inver x x₁ = refl
  ◀^-unique (◀S^ new1) (◀S^ new2) with refl◀^-unique new1 new2 = refl
  ◀^-unique (◀S∙ new1) (◀S∙ new2) with refl◀^-unique new1 new2 = refl
  ◀^-unique (◀S= new1 x) (◀S= new2 x₁) with refl◀^-unique new1 new2
                                       with refl↑ty-unique-inver x x₁ = refl
  ◀^-unique (◀S⋈ new1) (◀S⋈   new2) with refl◀^-unique new1 new2 = refl


  ◀^-⊆-total : Γ  Ω
              Ω  Δ
              Γ  k ^⇘ Γ'
              Δ  k ^⇘ Δ'
              ∃[ Ω' ](Ω  k ^⇘ Ω')
  ◀^-⊆-total (uvar ext1) (uvar ext2) (◀S∙ new1) (◀S∙ new2) =  ◀^-⊆-total ext1 ext2 new1 new2 .proj₁ ,∙ ,
                                                              ◀S∙ (◀^-⊆-total ext1 ext2 new1 new2 .proj₂) 
  ◀^-⊆-total (evar {Δ = Δ} ext1) (evar ext2) ◀Z ◀Z =  Δ , ◀Z 
  ◀^-⊆-total (evar ext1) (evar ext2) (◀S^ new1) (◀S^ new2) =  ◀^-⊆-total ext1 ext2 new1 new2 .proj₁ ,^ ,
                                                              ◀S^ (◀^-⊆-total ext1 ext2 new1 new2 .proj₂) 
  ◀^-⊆-total (evar ext1) (evar-sol ext2 regA) (◀S^ new1) (◀S= new2 x) =  ◀^-⊆-total ext1 ext2 new1 new2 .proj₁ ,^ ,
                                                                         ◀S^ (◀^-⊆-total ext1 ext2 new1 new2 .proj₂) 
  ◀^-⊆-total (evar-sol ext1 regA) (svar ext2 regA₁) (◀S^ new1) (◀S= {A = A} new2 x) =  ◀^-⊆-total ext1 ext2 new1 new2 .proj₁ ,= A ,
                                                                               ◀S= (◀^-⊆-total ext1 ext2 new1 new2 .proj₂) x 
  ◀^-⊆-total (svar ext1 regA) (svar ext2 regA₁) (◀S= new1 x) (◀S= {A = A} new2 x₁) =  ◀^-⊆-total ext1 ext2 new1 new2 .proj₁ ,= A ,
                                                                              ◀S= (◀^-⊆-total ext1 ext2 new1 new2 .proj₂) x₁ 
  ◀^-⊆-total (mark regΓ) (mark regΓ₁) (◀S⋈ {Γ' = Γ'} new1) (◀S⋈ new2) =  Γ'  , ◀S⋈ new1 


  inst-strengthen^ : [ B' / punchIn k X ] Γ  Δ
                    Γ  k ^⇘ Γ'
                    Δ  k ^⇘ Δ'
                    B ↑ty k  B'
                    [ B / X ] Γ'  Δ'
  inst-strengthen^ {k = #0} {#0} (⟹^S inst up1) ◀Z ◀Z upB
    with refl↑ty-unique-inver up1 upB = inst
  inst-strengthen^ {k = #0} {#S X} (⟹^S inst up1) ◀Z ◀Z upB
    with refl↑ty-unique-inver up1 upB = inst
  inst-strengthen^ {k = #S k} {#0} (⟹^0 up regA env) (◀S^ newΓ) (◀S= newΔ x) upB
    with refl◀^-unique newΓ newΔ = ⟹^0 (↑ty-comm1 upB up x) (⊢r-strengthen^ regA newΓ x) (sregular-strengthen^ env newΓ)
  inst-strengthen^ {k = #S k} {#S X} (⟹^S inst up1) (◀S^ newΓ) (◀S^ newΔ) upB
    with regAinst-⊢r inst
    with  preA , uppA ⊢r-◀^-↑ty-surjective regA newΓ = ⟹^S (inst-strengthen^ inst newΓ newΔ uppA) (↑ty-comm1 upB up1 uppA)
  inst-strengthen^ {k = #S k} {#S X} (⟹∙S inst up1) (◀S∙ newΓ) (◀S∙ newΔ) upB
    with regAinst-⊢r inst
    with  preA , uppA ⊢r-◀^-↑ty-surjective regA newΓ = ⟹∙S (inst-strengthen^ inst newΓ newΔ uppA) (↑ty-comm1 upB up1 uppA)
  inst-strengthen^ {k = #S k} {#S X} (⟹=S inst up1 regB) (◀S= newΓ x) (◀S= newΔ x₁) upB
    with refl↑ty-unique-inver x x₁
    with regAinst-⊢r inst
    with  preA , uppA ⊢r-◀^-↑ty-surjective regA newΓ = ⟹=S (inst-strengthen^ inst newΓ newΔ uppA) (↑ty-comm1 upB up1 uppA) (⊢r-strengthen^ regB newΓ x₁)


  ss-strengthen^ : Γ  A'    B'  Δ
                  Γ  k ^⇘ Γ'
                  Δ  k ^⇘ Δ'
                  A ↑ty k  A'
                  B ↑ty k  B'
                  Γ'  A    B  Δ'
  ss-strengthen^ (s-int regΓ) newΓ newΔ ↑ty-int ↑ty-int
    with refl◀^-unique newΓ newΔ = s-int (sregular-strengthen^ regΓ newΓ)
  ss-strengthen^ {B =  X} (s-var-∙ regΓ x) newΓ newΔ ↑ty-var upB
    with refl↑ty-var-inv upB refl
    with refl◀^-unique newΓ newΔ = s-var-∙ (sregular-strengthen^ regΓ newΓ) (∋∙-strengthen^ x newΓ)
  ss-strengthen^ (s-ex-l^ inst) newΓ newΔ ↑ty-var upB = s-ex-l^ (inst-strengthen^ inst newΓ newΔ upB)
  ss-strengthen^ (s-ex-r^ inst) newΓ newΔ upA ↑ty-var = s-ex-r^ (inst-strengthen^ inst newΓ newΔ upA)
  ss-strengthen^ (s-ex-l= regΓ x-in) newΓ newΔ ↑ty-var upB
    with refl◀^-unique newΓ newΔ = s-ex-l= (sregular-strengthen^ regΓ newΓ) (∋:=-strengthen^-reg regΓ x-in newΔ upB)
  ss-strengthen^ (s-ex-r= regΓ x-in) newΓ newΔ upA ↑ty-var
    with refl◀^-unique newΓ newΔ = s-ex-r= (sregular-strengthen^ regΓ newΓ) (∋:=-strengthen^-reg regΓ x-in newΔ upA)
  ss-strengthen^ (s-arr s s₁) newΓ newΔ (↑ty-arr upA upA₁) (↑ty-arr upB upB₁)
    with  Ω , newΩ ◀^-⊆-total (ss-⊆ s) (ss-⊆ s₁) newΓ newΔ = s-arr (ss-strengthen^ s newΓ newΩ upB upA) (ss-strengthen^ s₁ newΩ newΔ upA₁ upB₁)
  ss-strengthen^ (s-∀ s) newΓ newΔ (↑ty-∀ upA) (↑ty-∀ upB) = s-∀ (ss-strengthen^ s (◀S∙ newΓ) (◀S∙ newΔ) upA upB)

  ◀^-𝕣 : Γ  k ^⇘ Γ'
        𝕣 Γ  k ^⇘ 𝕣 Γ'
  ◀^-𝕣 ◀Z = ◀Z
  ◀^-𝕣 (◀S, new x) = ◀S, (◀^-𝕣 new) x
  ◀^-𝕣 (◀S^ new) = ◀S^ (◀^-𝕣 new)
  ◀^-𝕣 (◀S∙ new) = ◀S∙ (◀^-𝕣 new)
  ◀^-𝕣 (◀S= new x) = ◀S= (◀^-𝕣 new) x
  ◀^-𝕣 (◀S⋈ new) = new


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

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


  s-strengthen^ : Γ  A' ≤⁺ Σ'  Δ  B'
                 Γ  k ^⇘ Γ'
                 Δ  k ^⇘ Δ'
                 A ↑ty k  A'
                 B ↑ty k  B'
                 Σ ↑tyᶜ k  Σ'
                 Γ'  A ≤⁺ Σ  Δ'  B

  t-strengthen^ (⊢lit regΓ) new ↑ty-int ↑tyᵉ-lit ↑tyᶜ-□ = ⊢lit (tregular-strengthen^ regΓ new)
  t-strengthen^ (⊢var regΓ x∈Γ) new upA ↑tyᵉ-var ↑tyᶜ-□ = ⊢var (tregular-strengthen^ regΓ new) (∋⦂-strengthen^ x∈Γ regΓ new upA)
  t-strengthen^ (⊢ann ⊢e) newΓ upA (↑tyᵉ-⦂ upe up) ↑tyᶜ-□
    with regBt-⊢r ⊢e
    with  _ , uppB ⊢r-◀^-↑ty-surjective regB newΓ
    with refl↑ty-unique-inver upA up = ⊢ann (t-strengthen^ ⊢e newΓ uppB upe (↑tyᶜ-τ upA))
  t-strengthen^ (⊢app ⊢e) new upA (↑tyᵉ-app upe upe₁) upΣ
    with ⊢r-arr regA regA₁t-⊢r ⊢e
    with  _ , uppA ⊢r-◀^-↑ty-surjective regA new = ⊢app (t-strengthen^ ⊢e new (↑ty-arr uppA upA) upe (↑tyᶜ-e upe₁ upΣ))
  t-strengthen^ (⊢lam₁ ⊢e) new (↑ty-arr upA upA₁) (↑tyᵉ-ƛ upe) (↑tyᶜ-τ (↑ty-arr up-t up-t₁))
    with refl↑ty-unique-inver upA up-t = ⊢lam₁ (t-strengthen^ ⊢e (◀S, new up-t) upA₁ upe (↑tyᶜ-τ up-t₁))
  t-strengthen^ (⊢lam₂ ⊢e up-c ⊢e₁) new (↑ty-arr upA upA₁) (↑tyᵉ-ƛ upe) (↑tyᶜ-e {Σ = Σ} up-e upΣ)
    with  Σ' , upΣ' ↑tmᶜ0-total Σ = ⊢lam₂ (t-strengthen^ ⊢e new upA up-e ↑tyᶜ-□)
                                               upΣ'
                                               (t-strengthen^ ⊢e₁ (◀S, new upA) upA₁ upe (↑tmᶜ-↑tyᶜ-comm' upΣ' upΣ up-c))
  t-strengthen^ (⊢sub ⊢e ne gc s) new upA upe upΣ
    with regAt-⊢r ⊢e
    with  pA , uppA ⊢r-◀^-↑ty-surjective regA new
    = ⊢sub (t-strengthen^ ⊢e new uppA upe ↑tyᶜ-□) (nonempty-↑tyᶜ ne upΣ) (↑ty-gc' gc upe) (s-strengthen^ s (◀S⋈ new) (◀S⋈ new) uppA upA upΣ)
  t-strengthen^ (⊢tabs ⊢e) new (↑ty-∀ upA) (↑tyᵉ-Λ upe) ↑tyᶜ-□ = ⊢tabs (t-strengthen^ ⊢e (◀S∙ new) upA upe ↑tyᶜ-□)
  t-strengthen^ (⊢tabs-τ ⊢e) new (↑ty-∀ upA) (↑tyᵉ-Λ upe) (↑tyᶜ-τ (↑ty-∀ upA'))
    = ⊢tabs-τ (t-strengthen^ ⊢e (◀S∙ new) upA upe (↑tyᶜ-τ upA'))
  t-strengthen^ (⊢tapp ⊢e regA st s) newΓ upA (↑tyᵉ-⓪ upe upA₁) upΣ
    with regA't-⊢r ⊢e
    with  pA , ↑ty-∀ uppA ⊢r-◀^-↑ty-surjective regA' newΓ
    with  pB ,  uppB ⊢r-◀^-↑ty-surjective (st0-⊢r regA' st regA) newΓ
    = ⊢tapp (t-strengthen^ ⊢e newΓ (↑ty-∀ uppA) upe ↑tyᶜ-□) (↑ty-st-comm0'' regA upA₁ uppA uppB)
            (⊢r-strengthen^ st newΓ upA₁) (s-strengthen^ s (◀S⋈ newΓ) (◀S⋈ newΓ) uppB upA upΣ)

  s-strengthen^ (s-empty regΓ cloA grd) newΓ newΔ upA upB ↑tyᶜ-□
    with refl◀^-unique newΓ newΔ  = s-empty (sregular-strengthen^ regΓ newΓ) (⊢c-strengthen^ cloA newΓ upA) (≫-strengthen^ grd regΓ newΔ upA upB)
  s-strengthen^ (s-type ss) newΓ newΔ upA upB (↑tyᶜ-τ up-t)
    with refl↑ty-unique-inver upB up-t = s-type (ss-strengthen^ ss newΓ newΔ upA up-t)
  s-strengthen^ (s-term-c cloA ap ⊢e s) newΓ newΔ (↑ty-arr upA upA₁) (↑ty-arr upB upB₁) (↑tyᶜ-e up-e upΣ)
    with refl⊢id0 ⊢e
    = s-term-c (⊢c-strengthen^ cloA newΓ upA)
               (≫-strengthen^ ap (s-env-in s) newΓ upA upB)
               (t-strengthen^ ⊢e (◀^-𝕣 newΓ) upB up-e (↑tyᶜ-τ upB))
               (s-strengthen^ s newΓ newΔ upA₁ upB₁ upΣ)
  s-strengthen^ (s-term-o opnA ⊢e ss s) newΓ newΔ (↑ty-arr upA upA₁) (↑ty-arr upB upB₁) (↑tyᶜ-e up-e upΣ)
    with  Ω' , newΩ ◀^-⊆-total (ss-⊆ ss) (s-⊆ s) newΓ newΔ
    = s-term-o (⊢o-strengthen^ opnA newΓ upA)
               (t-strengthen^ ⊢e (◀^-𝕣 newΓ) upB up-e ↑tyᶜ-□)
               (ss-strengthen^ ss newΓ newΩ upB upA)
               (s-strengthen^ s newΩ newΔ upA₁ upB₁ upΣ)
  s-strengthen^ (s-∀l s upᶜ upᵉ upC upD) newΓ newΔ (↑ty-∀ upA) (↑ty-arr {A = A} {B = B} upB upB₁) (↑tyᶜ-e {e = e} {Σ = Σ} up-e upΣ)
    with reg-S= regΔ regBs-env-out s
    with  e' , upe ↑tyᵉ0-total e
    with  Σ' , upΣ' ↑tyᶜ0-total Σ
    with  A' , upA' ↑ty0-total A
    with  B' , upB' ↑ty0-total B
    with  pB , uppB ⊢r-◀^-↑ty-surjective regB newΔ
    = s-∀l (s-strengthen^ s (◀S^ newΓ) (◀S= newΔ uppB) upA
                          (↑ty-arr (↑ty-comm0' upB upC upA') (↑ty-comm0' upB₁ upD upB'))
                          (↑tyᶜ-e (↑tyᵉ-comm0' up-e upᵉ upe) (↑tyᶜ-comm0' upΣ upᶜ upΣ')))
           upΣ' upe upA' upB'
  s-strengthen^ (s-∀l-no s upᶜ upᵉ upC upD) newΓ newΔ (↑ty-∀ upA) (↑ty-arr {A = A} {B = B} upB upB₁) (↑tyᶜ-e {e = e} {Σ = Σ} up-e upΣ)
    with reg-S^ regΓs-env-out s
    with  e' , upe ↑tyᵉ0-total e
    with  Σ' , upΣ' ↑tyᶜ0-total Σ
    with  A' , upA' ↑ty0-total A
    with  B' , upB' ↑ty0-total B
    = s-∀l-no (s-strengthen^ s (◀S^ newΓ) (◀S^ newΔ) upA
                          (↑ty-arr (↑ty-comm0' upB upC upA') (↑ty-comm0' upB₁ upD upB'))
                          (↑tyᶜ-e (↑tyᵉ-comm0' up-e upᵉ upe) (↑tyᶜ-comm0' upΣ upᶜ upΣ')))
           upΣ' upe upA' upB'
  s-strengthen^ (s-svar-term inΓ s) newΓ newΔ ↑ty-var (↑ty-arr upB upB₁) (↑tyᶜ-e up-e upΣ)
    with refl◀^-unique newΓ newΔ
    with regA∋:=-⊢r (s-env-in s) inΓ
    with  pA , uppA ⊢r-◀^-↑ty-surjective regA newΔ
    = s-svar-term (∋:=-strengthen^-reg (s-env-in s) inΓ newΓ uppA) (s-strengthen^ s newΓ newΓ uppA (↑ty-arr upB upB₁) (↑tyᶜ-e up-e upΣ))
  s-strengthen^ (s-evar-infers infs inst) newΓ newΔ ↑ty-var upB (↑tyᶜ-e up-e upΣ)
    = s-evar-infers (infs-strengthen^ infs (◀^-𝕣 newΓ) upB (↑tyᶜ-e up-e upΣ)) (inst-strengthen^ inst newΓ newΔ upB)

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