{-# OPTIONS --safe --sized-types #-}

module Data.Var.Varlike where

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

open import Relation.Unary using (IUniversal; _⊢_; _⇒_)
open import Data.Var
open import Data.Pred as Pred hiding (All)
open import Data.Relation
open import Data.Environment
open import Generic.Syntax

private
  variable
    I : Set
    σ : I
    Γ Δ : List I
    𝓥 𝓥₁ 𝓥₂ 𝓒 𝓥ᴬ 𝓥ᴮ 𝓒ᴬ 𝓒ᴮ : I ─Scoped


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


  base : (Γ ─Env) 𝓥 Γ
  base {Γ = []}    = ε
  base {Γ = σ  Γ} = th^Env th^𝓥 base weaken  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 {I} Var
new   vl^Var = z
th^𝓥  vl^Var = th^Var

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


reify : VarLike 𝓥   Δ i  Kripke 𝓥 𝓒 Δ i Γ  Scope 𝓒 Δ i Γ
reify vl^𝓥 []         i b = b
reify vl^𝓥 Δ@(_  _)  i b = b (freshʳ vl^Var Δ) (freshˡ vl^𝓥 _)

module _ (vl^𝓥 : VarLike 𝓥) where

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

  weaken-is-fresh : All Eqᴿ Γ weaken (freshʳ vl^Var (σ  []))
  lookupᴿ weaken-is-fresh k = cong s (sym (lookup-base^Var k))

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

 freshʳ->> : (Δ : List I) {Γ Θ : List I}
             (ρ₁ : (Δ ─Env) 𝓥 Θ) (ρ₂ : (Γ ─Env) 𝓥 Θ) {i : I} (v : Var i Γ) 
             lookup (ρ₁ >> ρ₂) (lookup (freshʳ vl^Var Δ) v)  lookup ρ₂ v
 freshʳ->> Δ ρ₁ ρ₂ v = begin
   lookup (ρ₁ >> ρ₂) (lookup (freshʳ vl^Var Δ) v)
     ≡⟨ injectʳ->> ρ₁ ρ₂ (lookup (base vl^Var) v) 
   lookup ρ₂ (lookup (base vl^Var) v)
     ≡⟨ cong (lookup ρ₂) (lookup-base^Var v) 
   lookup ρ₂ v
     

module _ (𝓡^𝓥  : Rel 𝓥₁ 𝓥₂) where

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

   baseᴿ : All 𝓡^𝓥 Γ (base vl₁) (base vl₂)
   baseᴿ {[]   } = packᴿ λ ()
   baseᴿ {i  Γ} = (thᴿ weaken <$>ᴿ baseᴿ) ∙ᴿ newᴿ

   freshˡᴿ :  Γ  All 𝓡^𝓥 Δ (freshˡ vl₁ Γ) (freshˡ vl₂ Γ)
   freshˡᴿ n = thᴿ _ <$>ᴿ baseᴿ

   freshʳᴿ :  Γ  All 𝓡^𝓥 Δ (freshʳ vl₁ Γ) (freshʳ vl₂ Γ)
   freshʳᴿ n = thᴿ _ <$>ᴿ baseᴿ


module _ (vl^𝓥  : VarLike 𝓥) where

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


module _ (𝓥ᴾ : Pred {I} 𝓥) (𝓒ᴾ : Pred {I} 𝓒) where

 Kripkeᴾ :  Δ τ  ∀[ Kripke 𝓥 𝓒 Δ τ  const Set ]
 Kripkeᴾ []         τ k = pred 𝓒ᴾ τ k
 Kripkeᴾ Δ@(_  _)  τ k =  {Θ} th {ρ : (Δ ─Env) 𝓥 Θ}  Pred.All 𝓥ᴾ Δ ρ  pred 𝓒ᴾ τ (k th ρ)

module _ (𝓥ᴿ : Rel {I} 𝓥ᴬ 𝓥ᴮ) (𝓒ᴿ : Rel {I} 𝓒ᴬ 𝓒ᴮ) where

 Kripkeᴿ :  Δ i  ∀[ Kripke 𝓥ᴬ 𝓒ᴬ Δ i  Kripke 𝓥ᴮ 𝓒ᴮ Δ i  const Set ]
 Kripkeᴿ []         σ kᴬ kᴮ = rel 𝓒ᴿ σ kᴬ kᴮ
 Kripkeᴿ Δ@(_  _)  σ kᴬ kᴮ =  {Θ} (ρ : Thinning _ Θ) {vsᴬ vsᴮ} 
                              All 𝓥ᴿ Δ vsᴬ vsᴮ  rel 𝓒ᴿ σ (kᴬ ρ vsᴬ) (kᴮ ρ vsᴮ)