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

open import Level using (0ℓ)
open import Axiom.Extensionality.Propositional

module Unbox.Soundness (fext : Extensionality 0ℓ 0ℓ) where

open import Lib hiding (lookup)

open import Data.List.Properties as Lₚ
open import Data.Nat.Properties as ℕₚ

open import Unbox.Statics
open import Unbox.Semantics
open import Unbox.Restricted
open import Unbox.Gluing
open import Unbox.StaticProps
open import Unbox.SemanticProps fext
open import Unbox.GluingProps fext

lookup-Init :  {x}  x  T  Γ  InitialEnv Γ x  l′ T (len Γ  x  1)
lookup-Init here        = refl
lookup-Init (there T∈Γ) = lookup-Init T∈Γ

private
  I∼Inits-helper :  {x}  x  T  Γ  v x [ I ]  InitialEnv Γ x   T 》T (Γ  Γs)
  I∼Inits-helper {T} {Γ} {Γs} T∈Γ
    with split-∈ T∈Γ
  ...  | Γ′ , Γ″ , refl , refl
       rewrite lookup-Init T∈Γ = Bot⊆《》 _ (Γ  Γs) record
       { t∶T  = t[σ] (vlookup (length-∈ Γ′)) S-I
       ; krip = λ {Ψ′} {σ} ⊢σ  record
         { neu = v _
         ; ↘ne = Rl _ _
         ; ≈ne = subst  n  _  v (len Γ′) [ I ] [ σ ]  v (len (head Ψ′)  n  1)  T)
                       (sym (var-arith′ Γ′ T Γ″))
                       (≈-trans (≈-sym ([∘] (⊢r⇒⊢s ⊢σ) S-I (vlookup (length-∈ Γ′)))) (v∈Bot-gen _ (⊢r-comp (r-I I-≈) ⊢σ) refl))
         }
       }

I∼Inits-gen :  Γ Γs  I  InitialEnvs (Γ  Γs)   Γ  Γs 》Ψ (Γ  Γs)
I∼Inits-gen Γ [] = record
  { σ-wf  = S-I
  ; vlkup = I∼Inits-helper
  }
I∼Inits-gen Γ (Γ′  Γs) = record
  { σ-wf  = S-I
  ; vlkup = I∼Inits-helper
  ; Leq   = refl
  ; hds   = Γ  []
  ; Ψ|ρ0  = Γ′  Γs
  ; Ψ≡    = refl
  ; len≡  = refl
  ; rel   = I∼Inits-gen Γ′ Γs
  }

I∼Inits :  Ψ  I  InitialEnvs (toList Ψ)   Ψ 》Ψ Ψ
I∼Inits Ψ = I∼Inits-gen _ _

⊩⇒⊢ : Ψ  t  T  Ψ  t  T
⊩⇒⊢ ⊩t = helper (glu⇒⊢ _ tσ∼)
       where open Intp (⊩t  _ _ (I∼Inits _))
             helper : Ψ  t [ I ]  T  Ψ  t  T
             helper (t[σ] t∶T S-I) = t∶T

⊩s⇒⊢s : Ψ ⊩s σ  Ψ′  Ψ ⊢s σ  Ψ′
⊩s⇒⊢s ⊩σ = helper (glu⇒⊢s comp)
  where open Intps (⊩σ _ _ (I∼Inits _))
        helper : Ψ ⊢s σ  I  Ψ′  Ψ ⊢s σ  Ψ′
        helper (S-∘ S-I ⊢σ) = ⊢σ

vlookup′ :  {x} 
           x  T  Γ 
           ----------------
           Γ  Γs  v x  T
vlookup′ T∈Γ σ ρ σ∼ρ = record
  { ⟦t⟧  = lookup ρ _
  ; ↘⟦t⟧ = ⟦v⟧ _
  ; tσ∼  = glu⇒vlookup σ∼ρ T∈Γ
  }


⟶-I′ : (S  Γ)  Γs  t  T 
       ----------------------
       Γ  Γs  Λ t  S  T
⟶-I′ {t = t} ⊩t σ ρ σ∼ρ =
  let ⊢σ = glu⇒⊢s σ∼ρ
      ⊢t = ⊩⇒⊢ ⊩t
  in record
  { ⟦t⟧  = Λ _ ρ
  ; ↘⟦t⟧ = ⟦Λ⟧ _
  ; tσ∼  = record
    { t∶⟶  = t[σ] (⟶-I ⊢t) ⊢σ
    ; krip = λ ⊢δ s∼a 
      let ⊢s = glu⇒⊢ _ s∼a
          open Intp (⊩t _ _ (rel-↦ _ (《》Ψ-mon _ ⊢δ σ∼ρ) s∼a))
      in record
      { fa   = ⟦t⟧
      ; ↘fa  = Λ∙ ↘⟦t⟧
      ; rel  = 《》-resp-≈ _ tσ∼
                         (≈-trans ($-cong (≈-sym ([∘] (⊢r⇒⊢s ⊢δ) ⊢σ (⟶-I ⊢t))) (≈-refl ⊢s))
                         (≈-trans ($-cong (Λ-[] (S-∘ (⊢r⇒⊢s ⊢δ) ⊢σ) ⊢t) (≈-refl ⊢s))
                         (≈-trans (⟶-β (t[σ] ⊢t (⊢q (S-∘ (⊢r⇒⊢s ⊢δ) ⊢σ) _)) ⊢s)
                         (≈-trans (≈-sym ([∘] (S-, S-I ⊢s) (⊢q (S-∘ (⊢r⇒⊢s ⊢δ) ⊢σ) _) ⊢t))
                                  ([]-cong (≈-refl ⊢t) (⊢q∘I, (S-∘ (⊢r⇒⊢s ⊢δ) ⊢σ) ⊢s))))))
      }
    }
  }

⟶-E′ : Ψ  t  S  T 
       Ψ  s  S 
       -------------
       Ψ  t $ s  T
⟶-E′ ⊩t ⊩s σ ρ σ∼ρ =
  let ⊢σ = glu⇒⊢s σ∼ρ
      ⊢t = ⊩⇒⊢ ⊩t
      ⊢s = ⊩⇒⊢ ⊩s
  in record
          { ⟦t⟧  = fa
          ; ↘⟦t⟧ = ⟦$⟧ t.↘⟦t⟧ s.↘⟦t⟧ (subst (_∙ _  fa) (ap-vone _) ↘fa)
          ; tσ∼  = 《》-resp-≈ _ rel
                             (≈-trans ($-[] ⊢σ ⊢t ⊢s)
                                      ($-cong (≈-sym ([I] (t[σ] ⊢t ⊢σ))) (≈-refl (t[σ] ⊢s ⊢σ))))
          }
  where module t = Intp (⊩t σ ρ σ∼ρ)
        module s = Intp (⊩s σ ρ σ∼ρ)
        open Fun t.tσ∼
        open ap-rel (krip (r-I I-≈) s.tσ∼)

□-I′ : [] ∷⁺ Ψ  t  T 
       -----------------
       Ψ  box t   T
□-I′ {_} {t} ⊩t σ ρ σ∼ρ =
  let ⊢σ = glu⇒⊢s σ∼ρ
      ⊢t = ⊩⇒⊢ ⊩t
  in record
  { ⟦t⟧  = box ⟦t⟧
  ; ↘⟦t⟧ = ⟦box⟧ ↘⟦t⟧
  ; tσ∼  = record
    { t∶□  = t[σ] (□-I ⊢t) ⊢σ
    ; krip = λ {_ } {δ} Γs ⊢δ  record
      { ua  = ⟦t⟧ [ ins (mt δ) 1 ] [ ins vone (len Γs) ]
      ; ↘ua = box↘ (len Γs)
      ; rel = let σ∘δ   = S-∘ (⊢r⇒⊢s ⊢δ) ⊢σ
                  σδ;  = S-; ([]  []) σ∘δ refl
                  I;Γs = S-; Γs S-I refl
                  δ;Γs = S-; Γs (⊢r⇒⊢s ⊢δ) refl
              in 《》-resp-≈ _
                           (subst (_ ∼_∈  _ 》T _)
                                  (sym (trans (D-comp _ (ins (mt δ) 1) (ins vone (len Γs)))
                                              (cong (⟦t⟧ [_]) (ins-1-ø-ins-vone (mt δ) (len Γs)))))
                                  (《》-mon _ (r-; Γs ⊢δ (s≈-refl δ;Γs) refl) tσ∼))
                           (≈-trans (unbox-cong Γs
                                                (≈-trans (≈-sym ([∘] (⊢r⇒⊢s ⊢δ) ⊢σ (□-I ⊢t)))
                                                         (box-[] σ∘δ ⊢t))
                                                refl)
                           (≈-trans (□-β Γs (t[σ] ⊢t σδ;) refl)
                           (≈-trans (≈-sym ([∘] I;Γs σδ; ⊢t))
                           (≈-trans ([]-cong (≈-refl ⊢t) (;-∘ ([]  []) σ∘δ I;Γs refl))
                           (≈-trans ([]-cong (≈-refl ⊢t) (;-cong Γs (∘-I σ∘δ) (sym (+-identityʳ _))))
                           (≈-trans (≈-sym ([]-cong (≈-refl ⊢t) (;-∘ ([]  []) ⊢σ δ;Γs refl)))
                                    ([∘] δ;Γs (S-; ([]  []) ⊢σ refl) ⊢t)))))))
      }
    }
  }
  where open Intp (⊩t _ _ (rel-ext ([]  []) ρ σ∼ρ))

□-E′ :  {n} Γs 
       Ψ  t   T 
       len Γs  n 
       -------------------------
       Γs ++⁺ Ψ  unbox n t  T
□-E′ {_} {t} {_} {n} Γs ⊩t refl σ ρ σ∼ρ
  with Tr-《》 Γs σ∼ρ
...  | Φ₁ , Φ₂ , refl , eql , Trσ∼ =
  let ⊢σ   = glu⇒⊢s σ∼ρ
      ⊢t   = ⊩⇒⊢ ⊩t
      ↘ua′ = subst₂ (unbox∙_,_↘ ua)
                    (trans eql (O-《》 (len Γs) _ σ∼ρ (length-<-++⁺ Γs)))
                    (ap-vone _)
                    ↘ua
  in record
  { ⟦t⟧  = ua
  ; ↘⟦t⟧ = ⟦unbox⟧ n ↘⟦t⟧ ↘ua′
  ; tσ∼  = 《》-resp-≈ _ rel
                     (≈-trans (unbox-[] Γs ⊢σ ⊢t refl)
                     (subst  n  Φ₁ ++⁺ _  unbox n _  unbox _ _  _) eql
                            (unbox-cong Φ₁ (≈-sym ([I] t∶□)) refl)))
  }
  where open Intp (⊩t _ _ Trσ∼)
        open  tσ∼
        open unbox-rel (krip Φ₁ (r-I I-≈))

t[σ]′ : Ψ  t  T 
        Ψ′ ⊩s σ  Ψ 
        ----------------
        Ψ′  t [ σ ]  T
t[σ]′ ⊩t ⊩σ δ ρ δ∼ρ =
  let ⊢δ = glu⇒⊢s δ∼ρ
      ⊢t = ⊩⇒⊢ ⊩t
      ⊢σ = ⊩s⇒⊢s ⊩σ
  in record
  { ⟦t⟧  = t.⟦t⟧
  ; ↘⟦t⟧ = ⟦[]⟧ σ.↘⟦σ⟧ t.↘⟦t⟧
  ; tσ∼  = 《》-resp-≈ _ t.tσ∼ (≈-sym ([∘] ⊢δ ⊢σ ⊢t))
  }
  where module σ = Intps (⊩σ δ ρ δ∼ρ)
        module t = Intp (⊩t (_  δ) σ.⟦σ⟧ σ.comp)

S-I′ : Ψ ⊩s I  Ψ
S-I′ δ ρ δ∼ρ = record
  { ⟦σ⟧  = ρ
  ; ↘⟦σ⟧ = ⟦I⟧
  ; comp = 《》-resp-≈s _ δ∼ρ (I-∘ (glu⇒⊢s δ∼ρ))
  }

drop-rel :  Γs  σ  ρ   (T  Γ)  Γs 》Ψ Ψ  p  σ  drop ρ   Γ  Γs 》Ψ Ψ
drop-rel [] σ∼ρ        = record
  { σ-wf  = S-∘ σ-wf S-p
  ; vlkup = λ T∈Γ  《》-resp-≈ _ (vlkup (there T∈Γ)) (≈-trans ([∘] σ-wf S-p (vlookup T∈Γ)) ([]-cong ([p] T∈Γ) (s≈-refl σ-wf)))
  }
  where open Single σ∼ρ
drop-rel (Γ′  Γs) σ∼ρ = record
  { σ-wf  = S-∘ σ-wf S-p
  ; vlkup = λ T∈Γ  《》-resp-≈ _ (vlkup (there T∈Γ)) (≈-trans ([∘] σ-wf S-p (vlookup T∈Γ)) ([]-cong ([p] T∈Γ) (s≈-refl σ-wf)))
  ; Leq   = Leq
  ; hds   = hds
  ; Ψ|ρ0  = Ψ|ρ0
  ; Ψ≡    = Ψ≡
  ; len≡  = len≡
  ; rel   = 《》-resp-≈s _ rel (I-∘ (glu⇒⊢s rel))
  }
  where open Cons σ∼ρ

S-p′ : (T  Γ)  Γs ⊩s p  Γ  Γs
S-p′ {_} {σ} δ ρ δ∼ρ =
  record
  { ⟦σ⟧  = drop ρ
  ; ↘⟦σ⟧ = ⟦p⟧
  ; comp = drop-rel _ δ∼ρ
  }

S-,′ : Ψ ⊩s σ  Γ  Γs 
       Ψ  t  T 
       --------------------------
       Ψ ⊩s σ , t  (T  Γ)  Γs
S-,′ {_} {σ} {_} {_} {t} ⊩σ ⊩t δ ρ δ∼ρ =
  let ⊢δ = glu⇒⊢s δ∼ρ
      ⊢t = ⊩⇒⊢ ⊩t
      ⊢σ = ⊩s⇒⊢s ⊩σ
  in record
  { ⟦σ⟧  = σ.⟦σ⟧  t.⟦t⟧
  ; ↘⟦σ⟧ = ⟦,⟧ σ.↘⟦σ⟧ t.↘⟦t⟧
  ; comp = 《》-resp-≈s _ (rel-↦ _ σ.comp t.tσ∼) (,-∘ ⊢σ ⊢t ⊢δ)
  }
  where module σ = Intps (⊩σ δ ρ δ∼ρ)
        module t = Intp (⊩t δ ρ δ∼ρ)

S-;′ :  {n} Γs 
        Ψ ⊩s σ  Ψ′ 
        len Γs  n 
        -------------------------------
        Γs ++⁺ Ψ ⊩s σ  n  [] ∷⁺ Ψ′
S-;′ {_} {σ} {Ψ′} {n} Γs ⊩σ refl δ ρ δ∼ρ
  with Tr-《》 Γs δ∼ρ
...  | Φ₁ , Φ₂ , refl , eql , Trσ∼ =
  let ⊢δ   = glu⇒⊢s δ∼ρ
      ⊢σ   = ⊩s⇒⊢s ⊩σ
      eql′ = trans eql (O-《》 n _ δ∼ρ (length-<-++⁺ Γs))
  in record
  { ⟦σ⟧  = ext ⟦σ⟧ (O ρ n)
  ; ↘⟦σ⟧ = ⟦;⟧ ↘⟦σ⟧
  ; comp = 《》-resp-≈s (toList Ψ′)
                      (subst  m  (σ  Tr δ n)  len Φ₁  ext ⟦σ⟧ m   [] ∷⁺ Ψ′ 》Ψ (Φ₁ ++⁺ _)) eql′ (rel-ext Φ₁ _ comp))
                      (subst  m  _ ⊢s _  _  m  _) (sym eql) (;-∘ Γs ⊢σ ⊢δ refl))
  }
  where open Intps (⊩σ _ _ Trσ∼)

S-∘′ : Ψ ⊩s σ′  Ψ′ 
       Ψ′ ⊩s σ  Ψ″ 
       -----------------
       Ψ ⊩s σ  σ′  Ψ″
S-∘′ ⊩σ′ ⊩σ δ ρ δ∼ρ =
  let ⊢δ  = glu⇒⊢s δ∼ρ
      ⊢σ  = ⊩s⇒⊢s ⊩σ
      ⊢σ′ = ⊩s⇒⊢s ⊩σ′
  in record
  { ⟦σ⟧  = σ.⟦σ⟧
  ; ↘⟦σ⟧ = ⟦∘⟧ σ′.↘⟦σ⟧ σ.↘⟦σ⟧
  ; comp = 《》-resp-≈s _ σ.comp (∘-assoc ⊢δ ⊢σ′ ⊢σ)
  }
  where module σ′ = Intps (⊩σ′ δ ρ δ∼ρ)
        module σ  = Intps (⊩σ _ _ σ′.comp)

-- fundamental theorem
mutual
  fund-⊢ : Ψ  t  T  Ψ  t  T
  fund-⊢ (vlookup T∈Γ)   = vlookup′ T∈Γ
  fund-⊢ (⟶-I t∶T)       = ⟶-I′ (fund-⊢ t∶T)
  fund-⊢ (⟶-E t∶F s∶S)   = ⟶-E′ (fund-⊢ t∶F) (fund-⊢ s∶S)
  fund-⊢ (□-I t∶T)       = □-I′ (fund-⊢ t∶T)
  fund-⊢ (□-E Γs t∶T eq) = □-E′ Γs (fund-⊢ t∶T) eq
  fund-⊢ (t[σ] t∶T σ∶Ψ′) = t[σ]′ (fund-⊢ t∶T) (fund-⊢s σ∶Ψ′)

  fund-⊢s : Ψ ⊢s σ  Ψ′  Ψ ⊩s σ  Ψ′
  fund-⊢s S-I               = S-I′
  fund-⊢s S-p               = S-p′
  fund-⊢s (S-, σ∶Ψ′ t∶T)    = S-,′ (fund-⊢s σ∶Ψ′) (fund-⊢ t∶T)
  fund-⊢s (S-; Γs σ∶Ψ′ eq) = S-;′ Γs (fund-⊢s σ∶Ψ′) eq
  fund-⊢s (S-∘ σ∶Ψ′ σ′∶Ψ″)  = S-∘′ (fund-⊢s σ∶Ψ′) (fund-⊢s σ′∶Ψ″)


record Soundness Ψ ρ t T : Set where
  field
    nf  : Nf
    nbe : Nbe (map len Ψ) ρ t T nf
    ≈nf : Ψ  t  Nf⇒Exp nf  T

soundness : Ψ  t  T  Soundness Ψ (InitialEnvs (toList Ψ)) t T
soundness t∶T = record
  { nf  = nf
  ; nbe = record
    { ⟦t⟧ = ⟦t⟧
    ; ↘⟦t⟧ = ↘⟦t⟧
    ; ↓⟦t⟧ = subst  a  Rf _ -  _ a  nf) (ap-vone _) ↘nf
    }
  ; ≈nf = ≈-trans (≈-sym ([I] t∶T)) ≈nf
  }
  where open Intp (fund-⊢ t∶T _ _ (I∼Inits _))
        open Top (《》⊆Top _  _ (《》-resp-≈ _ tσ∼ (≈-sym ([I] t∶T)))) using (krip)
        open TopPred (krip (r-I I-≈))

nbe-comp : Ψ  t  T   λ w  Nbe (map len Ψ) (InitialEnvs (toList Ψ)) t T w
nbe-comp t∶T = nf , nbe
  where open Soundness (soundness t∶T)