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

module Definition.Typed.Weakening where

open import Definition.Untyped as U hiding (wk)
open import Definition.Untyped.Properties
open import Definition.Typed

open import Tools.Nat
open import Tools.Product
import Tools.PropositionalEquality as PE


-- Weakening type

data _∷_⊆_ : Wk  Con Term  Con Term  Set where
  id   :  {Γ}        id  Γ  Γ
  step :  {Γ Δ A ρ}  ρ   Δ  Γ  step ρ  Δ  A         Γ
  lift :  {Γ Δ A ρ}  ρ   Δ  Γ  lift ρ  Δ  U.wk ρ A  Γ  A


-- -- Weakening composition

_•ₜ_ :  {ρ ρ′ Γ Δ Δ′}  ρ  Γ  Δ  ρ′  Δ  Δ′  ρ  ρ′  Γ  Δ′
id     •ₜ η′ = η′
step η •ₜ η′ = step (η •ₜ η′)
lift η •ₜ id = lift η
lift η •ₜ step η′ = step (η •ₜ η′)
_•ₜ_ {lift ρ} {lift ρ′} {Δ′ = Δ′  A} (lift η) (lift η′) =
  PE.subst  x  lift (ρ  ρ′)  x  Δ′  A)
           (PE.cong₂ _∙_ PE.refl (PE.sym (wk-comp ρ ρ′ A)))
           (lift (η •ₜ η′))


-- Weakening of judgements

wkIndex :  {Γ Δ n A ρ}  ρ  Δ  Γ 
        let ρA = U.wk ρ A
            ρn = wkVar ρ n
        in   Δ  n  A  Γ  ρn  ρA  Δ
wkIndex id ⊢Δ i = PE.subst  x  _  x  _) (PE.sym (wk-id _)) i
wkIndex (step ρ) (⊢Δ  A) i = PE.subst  x  _  x  _)
                                       (wk1-wk _ _)
                                       (there (wkIndex ρ ⊢Δ i))
wkIndex (lift ρ) (⊢Δ  A) (there i) = PE.subst  x  _  x  _)
                                               (wk1-wk≡lift-wk1 _ _)
                                               (there (wkIndex ρ ⊢Δ i))
wkIndex (lift ρ) ⊢Δ here =
  let G = _
      n = _
  in  PE.subst  x  n  x  G)
               (wk1-wk≡lift-wk1 _ _)
               here

mutual
  wk :  {Γ Δ A ρ}  ρ  Δ  Γ 
     let ρA = U.wk ρ A
     in   Δ  Γ  A  Δ  ρA
  wk ρ ⊢Δ ( ⊢Γ) =  ⊢Δ
  wk ρ ⊢Δ (U ⊢Γ) = U ⊢Δ
  wk ρ ⊢Δ (Π F  G) = let ρF = wk ρ ⊢Δ F
                      in  Π ρF  (wk (lift ρ) (⊢Δ  ρF) G)
  wk ρ ⊢Δ (univ A) = univ (wkTerm ρ ⊢Δ A)

  wkTerm :  {Γ Δ A t ρ}  ρ  Δ  Γ 
         let ρA = U.wk ρ A
             ρt = U.wk ρ t
         in  Δ  Γ  t  A  Δ  ρt  ρA
  wkTerm ρ ⊢Δ ( ⊢Γ) =  ⊢Δ
  wkTerm ρ ⊢Δ (Π F  G) = let ρF = wkTerm ρ ⊢Δ F
                          in  Π ρF  (wkTerm (lift ρ) (⊢Δ  univ ρF) G)
  wkTerm ρ ⊢Δ (var ⊢Γ x) = var ⊢Δ (wkIndex ρ ⊢Δ x)
  wkTerm ρ ⊢Δ (lam F t) = let ρF = wk ρ ⊢Δ F
                          in lam ρF (wkTerm (lift ρ) (⊢Δ  ρF) t)
  wkTerm ρ ⊢Δ (_∘_ {G = G} g a) = PE.subst  x  _  _  x)
                                           (PE.sym (wk-β G))
                                           (wkTerm ρ ⊢Δ g  wkTerm ρ ⊢Δ a)
  wkTerm ρ ⊢Δ (zero ⊢Γ) = zero ⊢Δ
  wkTerm ρ ⊢Δ (suc n) = suc (wkTerm ρ ⊢Δ n)
  wkTerm {Δ = Δ} {ρ = ρ} [ρ] ⊢Δ (natrec {G = G} {s = s} ⊢G ⊢z ⊢s ⊢n) =
    PE.subst  x  _  natrec _ _ _ _  x) (PE.sym (wk-β G))
             (natrec (wk (lift [ρ]) (⊢Δ   ⊢Δ) ⊢G)
                     (PE.subst  x  _  _  x) (wk-β G) (wkTerm [ρ] ⊢Δ ⊢z))
                     (PE.subst  x  Δ  U.wk ρ s  x)
                               (wk-β-natrec ρ G)
                               (wkTerm [ρ] ⊢Δ ⊢s))
                     (wkTerm [ρ] ⊢Δ ⊢n))
  wkTerm ρ ⊢Δ (conv t A≡B) = conv (wkTerm ρ ⊢Δ t) (wkEq ρ ⊢Δ A≡B)

  wkEq :  {Γ Δ A B ρ}  ρ  Δ  Γ 
       let ρA = U.wk ρ A
           ρB = U.wk ρ B
       in  Δ  Γ  A  B  Δ  ρA  ρB
  wkEq ρ ⊢Δ (univ A≡B) = univ (wkEqTerm ρ ⊢Δ A≡B)
  wkEq ρ ⊢Δ (refl A) = refl (wk ρ ⊢Δ A)
  wkEq ρ ⊢Δ (sym A≡B) = sym (wkEq ρ ⊢Δ A≡B)
  wkEq ρ ⊢Δ (trans A≡B B≡C) = trans (wkEq ρ ⊢Δ A≡B) (wkEq ρ ⊢Δ B≡C)
  wkEq ρ ⊢Δ (Π-cong F F≡H G≡E) = let ρF = wk ρ ⊢Δ F
                                 in  Π-cong ρF (wkEq ρ ⊢Δ F≡H)
                                               (wkEq (lift ρ) (⊢Δ  ρF) G≡E)

  wkEqTerm :  {Γ Δ A t u ρ}  ρ  Δ  Γ 
           let ρA = U.wk ρ A
               ρt = U.wk ρ t
               ρu = U.wk ρ u
           in  Δ  Γ  t  u  A  Δ  ρt  ρu  ρA
  wkEqTerm ρ ⊢Δ (refl t) = refl (wkTerm ρ ⊢Δ t)
  wkEqTerm ρ ⊢Δ (sym t≡u) = sym (wkEqTerm ρ ⊢Δ t≡u)
  wkEqTerm ρ ⊢Δ (trans t≡u u≡r) = trans (wkEqTerm ρ ⊢Δ t≡u) (wkEqTerm ρ ⊢Δ u≡r)
  wkEqTerm ρ ⊢Δ (conv t≡u A≡B) = conv (wkEqTerm ρ ⊢Δ t≡u) (wkEq ρ ⊢Δ A≡B)
  wkEqTerm ρ ⊢Δ (Π-cong F F≡H G≡E) =
    let ρF = wk ρ ⊢Δ F
    in  Π-cong ρF (wkEqTerm ρ ⊢Δ F≡H)
                  (wkEqTerm (lift ρ) (⊢Δ  ρF) G≡E)
  wkEqTerm ρ ⊢Δ (app-cong {G = G} f≡g a≡b) =
    PE.subst  x  _  _  _  x)
             (PE.sym (wk-β G))
             (app-cong (wkEqTerm ρ ⊢Δ f≡g) (wkEqTerm ρ ⊢Δ a≡b))
  wkEqTerm ρ ⊢Δ (β-red {a = a} {t = t} {G = G} F ⊢t ⊢a) =
    let ρF = wk ρ ⊢Δ F
    in  PE.subst  x  _  _  _  x)
                 (PE.sym (wk-β G))
                 (PE.subst  x  _  U.wk _ ((lam t)  a)  x  _)
                           (PE.sym (wk-β t))
                           (β-red ρF (wkTerm (lift ρ) (⊢Δ  ρF) ⊢t)
                                     (wkTerm ρ ⊢Δ ⊢a)))
  wkEqTerm ρ ⊢Δ (η-eq F f g f0≡g0) =
    let ρF = wk ρ ⊢Δ F
    in  η-eq ρF (wkTerm ρ ⊢Δ f)
                (wkTerm ρ ⊢Δ g)
                (PE.subst  t  _  t  _  _  _)
                          (PE.sym (wk1-wk≡lift-wk1 _ _))
                          (PE.subst  t  _  _  t  _  _)
                                    (PE.sym (wk1-wk≡lift-wk1 _ _))
                                    (wkEqTerm (lift ρ) (⊢Δ  ρF) f0≡g0)))
  wkEqTerm ρ ⊢Δ (suc-cong m≡n) = suc-cong (wkEqTerm ρ ⊢Δ m≡n)
  wkEqTerm {Δ = Δ} {ρ = ρ} [ρ] ⊢Δ (natrec-cong {s = s} {s′ = s′} {F = F}
                                     F≡F′ z≡z′ s≡s′ n≡n′) =
    PE.subst  x  Δ  natrec _ _ _ _  _  x) (PE.sym (wk-β F))
             (natrec-cong (wkEq (lift [ρ]) (⊢Δ   ⊢Δ) F≡F′)
                          (PE.subst  x  Δ  _  _  x) (wk-β F)
                                    (wkEqTerm [ρ] ⊢Δ z≡z′))
                          (PE.subst  x  Δ  U.wk ρ s
                                              U.wk ρ s′  x)
                                    (wk-β-natrec _ F)
                                    (wkEqTerm [ρ] ⊢Δ s≡s′))
                          (wkEqTerm [ρ] ⊢Δ n≡n′))
  wkEqTerm {Δ = Δ} {ρ = ρ} [ρ] ⊢Δ (natrec-zero {z} {s} {F} ⊢F ⊢z ⊢s) =
    PE.subst  x  Δ  natrec (U.wk (lift _) F) _ _ _  _  x)
             (PE.sym (wk-β F))
             (natrec-zero (wk (lift [ρ]) (⊢Δ   ⊢Δ) ⊢F)
                          (PE.subst  x  Δ  U.wk ρ z  x)
                                    (wk-β F)
                                    (wkTerm [ρ] ⊢Δ ⊢z))
                          (PE.subst  x  Δ  U.wk ρ s  x)
                                    (wk-β-natrec _ F)
                                    (wkTerm [ρ] ⊢Δ ⊢s)))
  wkEqTerm {Δ = Δ} {ρ = ρ} [ρ] ⊢Δ (natrec-suc {n} {z} {s} {F} ⊢n ⊢F ⊢z ⊢s) =
    PE.subst  x  Δ  natrec (U.wk (lift _) F) _ _ _
                       _  (natrec _ _ _ _)  x)
             (PE.sym (wk-β F))
             (natrec-suc (wkTerm [ρ] ⊢Δ ⊢n)
                         (wk (lift [ρ]) (⊢Δ   ⊢Δ) ⊢F)
                         (PE.subst  x  Δ  U.wk ρ z  x)
                                   (wk-β F)
                                   (wkTerm [ρ] ⊢Δ ⊢z))
                         (PE.subst  x  Δ  U.wk ρ s  x)
                                   (wk-β-natrec _ F)
                                   (wkTerm [ρ] ⊢Δ ⊢s)))

mutual
  wkRed :  {Γ Δ A B ρ}  ρ  Δ  Γ 
           let ρA = U.wk ρ A
               ρB = U.wk ρ B
           in  Δ  Γ  A  B  Δ  ρA  ρB
  wkRed ρ ⊢Δ (univ A⇒B) = univ (wkRedTerm ρ ⊢Δ A⇒B)

  wkRedTerm :  {Γ Δ A t u ρ}  ρ  Δ  Γ 
           let ρA = U.wk ρ A
               ρt = U.wk ρ t
               ρu = U.wk ρ u
           in  Δ  Γ  t  u  A  Δ  ρt  ρu  ρA
  wkRedTerm ρ ⊢Δ (conv t⇒u A≡B) = conv (wkRedTerm ρ ⊢Δ t⇒u) (wkEq ρ ⊢Δ A≡B)
  wkRedTerm ρ ⊢Δ (app-subst {B = B} t⇒u a) =
    PE.subst  x  _  _  _  x) (PE.sym (wk-β B))
             (app-subst (wkRedTerm ρ ⊢Δ t⇒u) (wkTerm ρ ⊢Δ a))
  wkRedTerm ρ ⊢Δ (β-red {A} {B} {a} {t} ⊢A ⊢t ⊢a) =
    let ⊢ρA = wk ρ ⊢Δ ⊢A
    in  PE.subst  x  _  _  _  x) (PE.sym (wk-β B))
                 (PE.subst  x  _  U.wk _ ((lam t)  a)  x  _)
                           (PE.sym (wk-β t))
                           (β-red ⊢ρA (wkTerm (lift ρ) (⊢Δ  ⊢ρA) ⊢t)
                                      (wkTerm ρ ⊢Δ ⊢a)))
  wkRedTerm {Δ = Δ} {ρ = ρ} [ρ] ⊢Δ (natrec-subst {s = s} {F = F} ⊢F ⊢z ⊢s n⇒n′) =
    PE.subst  x  _  natrec _ _ _ _  _  x) (PE.sym (wk-β F))
             (natrec-subst (wk (lift [ρ]) (⊢Δ   ⊢Δ) ⊢F)
                           (PE.subst  x  _  _  x) (wk-β F)
                                     (wkTerm [ρ] ⊢Δ ⊢z))
                           (PE.subst  x  Δ  U.wk ρ s  x)
                                     (wk-β-natrec _ F)
                                     (wkTerm [ρ] ⊢Δ ⊢s))
                           (wkRedTerm [ρ] ⊢Δ n⇒n′))
  wkRedTerm {Δ = Δ} {ρ = ρ} [ρ] ⊢Δ (natrec-zero {s = s} {F = F} ⊢F ⊢z ⊢s) =
    PE.subst  x  _  natrec (U.wk (lift ρ) F) _ _ _  _  x)
             (PE.sym (wk-β F))
             (natrec-zero (wk (lift [ρ]) (⊢Δ   ⊢Δ) ⊢F)
                          (PE.subst  x  _  _  x)
                                    (wk-β F)
                                    (wkTerm [ρ] ⊢Δ ⊢z))
                          (PE.subst  x  Δ  U.wk ρ s  x)
                                    (wk-β-natrec ρ F)
                                    (wkTerm [ρ] ⊢Δ ⊢s)))
  wkRedTerm {Δ = Δ} {ρ = ρ} [ρ] ⊢Δ (natrec-suc {s = s} {F = F} ⊢n ⊢F ⊢z ⊢s) =
    PE.subst  x  _  natrec _ _ _ _  _  natrec _ _ _ _  x)
             (PE.sym (wk-β F))
             (natrec-suc (wkTerm [ρ] ⊢Δ ⊢n)
                         (wk (lift [ρ]) (⊢Δ   ⊢Δ) ⊢F)
                         (PE.subst  x  _  _  x)
                                   (wk-β F)
                                   (wkTerm [ρ] ⊢Δ ⊢z))
                         (PE.subst  x  Δ  U.wk ρ s  x)
                                   (wk-β-natrec ρ F)
                                   (wkTerm [ρ] ⊢Δ ⊢s)))

wkRed* :  {Γ Δ A B ρ}  ρ  Δ  Γ 
           let ρA = U.wk ρ A
               ρB = U.wk ρ B
           in  Δ  Γ  A ⇒* B  Δ  ρA ⇒* ρB
wkRed* ρ ⊢Δ (id A) = id (wk ρ ⊢Δ A)
wkRed* ρ ⊢Δ (A⇒A′  A′⇒*B) = wkRed ρ ⊢Δ A⇒A′  wkRed* ρ ⊢Δ A′⇒*B

wkRed*Term :  {Γ Δ A t u ρ}  ρ  Δ  Γ 
           let ρA = U.wk ρ A
               ρt = U.wk ρ t
               ρu = U.wk ρ u
           in  Δ  Γ  t ⇒* u  A  Δ  ρt ⇒* ρu  ρA
wkRed*Term ρ ⊢Δ (id t) = id (wkTerm ρ ⊢Δ t)
wkRed*Term ρ ⊢Δ (t⇒t′  t′⇒*u) = wkRedTerm ρ ⊢Δ t⇒t′  wkRed*Term ρ ⊢Δ t′⇒*u

wkRed:*: :  {Γ Δ A B ρ}  ρ  Δ  Γ 
         let ρA = U.wk ρ A
             ρB = U.wk ρ B
         in  Δ  Γ  A :⇒*: B  Δ  ρA :⇒*: ρB
wkRed:*: ρ ⊢Δ [ ⊢A , ⊢B , D ] = [ wk ρ ⊢Δ ⊢A , wk ρ ⊢Δ ⊢B , wkRed* ρ ⊢Δ D ]

wkRed:*:Term :  {Γ Δ A t u ρ}  ρ  Δ  Γ 
             let ρA = U.wk ρ A
                 ρt = U.wk ρ t
                 ρu = U.wk ρ u
             in  Δ  Γ  t :⇒*: u  A  Δ  ρt :⇒*: ρu  ρA
wkRed:*:Term ρ ⊢Δ [ ⊢t , ⊢u , d ] =
  [ wkTerm ρ ⊢Δ ⊢t , wkTerm ρ ⊢Δ ⊢u , wkRed*Term ρ ⊢Δ d ]