module varlike where

open import Data.List.Base hiding (lookup ; [_])
open import Data.Sum
open import Function
open import Relation.Binary.PropositionalEquality hiding ([_])

open import indexed
open import var
open import pred hiding (∀[_])
open import rel
open import environment


module _ {I : Set} where

 record VarLike (𝓥 : I ─Scoped) : Set where
   field  new   : {i : I}  [ (i ∷_)  𝓥 i ]
          th^𝓥  : {i : I}  Thinnable (𝓥 i)

   base :  {Γ}  (Γ ─Env) 𝓥 Γ
   base {[]}  = ε
   base {σ  Γ} = th^Env th^𝓥 base extend  new

   freshʳ : (Δ : List I)   {Γ}  (Γ ─Env) 𝓥 (Δ ++ Γ)
   freshʳ Δ = th^Env th^𝓥 base (pack (injectʳ Δ))

   freshˡ : (Δ : List I)   {Γ}  (Γ ─Env) 𝓥 (Γ ++ Δ)
   freshˡ k = th^Env th^𝓥 base (pack (injectˡ _))

   singleton :  {Γ σ}  𝓥 σ Γ  (σ  Γ ─Env) 𝓥 Γ
   singleton v = base  v
 open VarLike public

 vl^Var : VarLike Var
 new   vl^Var = z
 th^𝓥  vl^Var = th^Var

 lookup-base^Var : {Γ : List I} {σ : I} (k : Var σ Γ)  lookup (base vl^Var) k  k
 lookup-base^Var z     = refl
 lookup-base^Var (s k) = cong s (lookup-base^Var k)

module _ {I : Set} {𝓥 : I ─Scoped} (vl^𝓥 : VarLike 𝓥) where

 lift : (Θ : List I)   {Γ Δ}  (Γ ─Env) 𝓥 Δ  (Θ ++ Γ ─Env) 𝓥 (Θ ++ Δ)
 lift Θ {Γ} {Δ} ρ = freshˡ vl^𝓥 Δ {Θ} >> th^Env (th^𝓥 vl^𝓥) ρ (freshʳ vl^Var Θ)

module _ {I : Set} {σ : I} {Γ : List I} where

  extend-is-fresh : ∀[ Eq^R ] (Thinning Γ (σ  Γ)  extend) (freshʳ vl^Var (σ  []))
  lookup^R extend-is-fresh k = cong s (sym (lookup-base^Var k))

module _ {I : Set} {𝓥 : I ─Scoped} where
 open ≡-Reasoning

 split-freshʳ : (Δ : List I) {Γ : List I} {i : I} (v : Var i Γ) 
                split Δ (lookup (freshʳ vl^Var Δ) v)  inj₂ v
 split-freshʳ Δ v =
   begin
     split Δ (injectʳ Δ (lookup (base vl^Var) v)) ≡⟨ split-injectʳ Δ (lookup (base vl^Var) v) 
     inj₂ (lookup (base vl^Var) v)                ≡⟨ cong inj₂ (lookup-base^Var v) 
     inj₂ v
   

 freshʳ->> : (Δ : List I) {Γ Θ : List I}
             (ρ₁ : (Δ ─Env) 𝓥 Θ) (ρ₂ : (Γ ─Env) 𝓥 Θ) {i : I} (v : Var i Γ) 
             lookup (ρ₁ >> ρ₂) (lookup (freshʳ vl^Var Δ) v)  lookup ρ₂ v
 freshʳ->> Δ ρ₁ ρ₂ v rewrite split-freshʳ Δ v = refl

module _ {I : Set} {𝓥₁ 𝓥₂ : I ─Scoped} (𝓡^𝓥  : Rel 𝓥₁ 𝓥₂) where

 record VarLike^R (vl₁ : VarLike 𝓥₁) (vl₂ : VarLike 𝓥₂) : Set where
   field  new^R  : {i : I} {Γ : List I}  rel 𝓡^𝓥 {i} {i  Γ} (new vl₁) (new vl₂)
          th^R   : {i : I} {Γ Δ : List I} (σ : Thinning Γ Δ) {v₁ : 𝓥₁ i Γ} {v₂ : 𝓥₂ i Γ} 
                   rel 𝓡^𝓥 v₁ v₂  rel 𝓡^𝓥 (th^𝓥 vl₁ v₁ σ) (th^𝓥 vl₂ v₂ σ)

   base^R : {Γ : List I}  ∀[ 𝓡^𝓥 ] (base vl₁ {Γ}) (base vl₂)
   base^R {[]   } = pack^R λ ()
   base^R {i  Γ} = (th^R extend <$>^R base^R) ∙^R new^R

   freshˡ^R : (Γ : List I) {Δ : List I}  ∀[ 𝓡^𝓥 ] (freshˡ vl₁ Γ {Δ}) (freshˡ vl₂ Γ)
   freshˡ^R n = th^R _ <$>^R base^R

   freshʳ^R : (Γ : List I) {Δ : List I}  ∀[ 𝓡^𝓥 ] (freshʳ vl₁ Γ {Δ}) (freshʳ vl₂ Γ)
   freshʳ^R n = th^R _ <$>^R base^R


module _ {I : Set} {𝓥 : I ─Scoped} (vl^𝓥  : VarLike 𝓥) where

 vl^Refl : VarLike^R Eq^R vl^𝓥 vl^𝓥
 VarLike^R.new^R  vl^Refl = refl
 VarLike^R.th^R   vl^Refl = λ σ  cong  v  th^𝓥 vl^𝓥 v σ)


module _ {I : Set} {𝓥 𝓒 : I ─Scoped} (𝓥^P  : Pred 𝓥) (𝓒^P : Pred 𝓒) where


 Kripke^P : (Δ : List I) (τ : I)  [ Kripke 𝓥 𝓒 Δ τ  κ Set ]
 Kripke^P []       σ k = pred 𝓒^P k
 Kripke^P (τ  Δ)  σ k = {Θ : List I}   th {ρ}  pred.∀[ 𝓥^P ] ρ  pred 𝓒^P {σ} {Θ} (k th ρ)


module _ {I : Set} {𝓥₁ 𝓥₂ 𝓒₁ 𝓒₂ : I ─Scoped} (𝓡^𝓥  : Rel 𝓥₁ 𝓥₂) (𝓡^𝓒  : Rel 𝓒₁ 𝓒₂) where


 Kripke^R : (Δ : List I) (τ : I)  [ Kripke 𝓥₁ 𝓒₁ Δ τ  Kripke 𝓥₂ 𝓒₂ Δ τ  κ Set ]
 Kripke^R []         σ k₁ k₂ = rel 𝓡^𝓒 k₁ k₂
 Kripke^R Δ@(_  _)  σ k₁ k₂ = {Θ : List I} {ρ₁ : (Δ ─Env) 𝓥₁ Θ} {ρ₂ : (Δ ─Env) 𝓥₂ Θ}   th  ∀[ 𝓡^𝓥 ] ρ₁ ρ₂  rel 𝓡^𝓒 (k₁ th ρ₁) (k₂ th ρ₂)