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

open import Data.Var hiding (z; s; _<$>_)

module Generic.Fusion {I : Set} {𝓥ᴬ 𝓥ᴮ 𝓥ᴬᴮ 𝓒ᴬ 𝓒ᴮ 𝓒ᴬᴮ : I ─Scoped} where

open import Size
open import Data.List hiding ([_] ; zip ; lookup)
open import Function renaming (_∘′_ to _∘_) hiding (_∘_)
open import Relation.Binary.PropositionalEquality hiding ([_])

open import Relation.Unary
open import Data.Relation hiding (_>>ᴿ_)
open import Data.Var.Varlike
open import Data.Environment

open import Generic.Syntax
open import Generic.Semantics
open import Generic.Semantics.Syntactic
open import Generic.Relator

private
  variable
    s : Size
    σ τ : I
    Γ Δ Θ Ω : List I
    ρᴬ : (Γ ─Env) 𝓥ᴬ Δ
    ρᴮ : (Δ ─Env) 𝓥ᴮ Θ
    ρᴬᴮ : (Γ ─Env) 𝓥ᴬᴮ Θ
    vsᴬᴮ : (Δ ─Env) 𝓥ᴬᴮ Γ
    vsᴮ : (Δ ─Env) 𝓥ᴮ Γ


record Fusion (d : Desc I) (𝓢ᴬ : Semantics d 𝓥ᴬ 𝓒ᴬ) (𝓢ᴮ : Semantics d 𝓥ᴮ 𝓒ᴮ)
  (𝓢ᴬᴮ : Semantics d 𝓥ᴬᴮ 𝓒ᴬᴮ)
  (𝓔ᴿ :  Γ Δ {Θ}  (Γ ─Env) 𝓥ᴬ Δ  (Δ ─Env) 𝓥ᴮ Θ  (Γ ─Env) 𝓥ᴬᴮ Θ  Set)
  (𝓥ᴿ : Rel 𝓥ᴮ 𝓥ᴬᴮ) (𝓒ᴿ : Rel 𝓒ᴮ 𝓒ᴬᴮ) : Set where

  module 𝓢ᴬ = Semantics 𝓢ᴬ
  module 𝓢ᴮ = Semantics 𝓢ᴮ
  module 𝓢ᴬᴮ = Semantics 𝓢ᴬᴮ
  evalᴬ = 𝓢ᴬ.semantics
  evalᴮ = 𝓢ᴮ.semantics
  evalᴬᴮ = 𝓢ᴬᴮ.semantics
  field

    reifyᴬ  :   σ  ∀[ 𝓒ᴬ σ  Tm d  σ ]

    vl^𝓥ᴬ :  VarLike 𝓥ᴬ

  quoteᴬ :  Δ i  Kripke 𝓥ᴬ 𝓒ᴬ Δ i Γ  Tm d  i (Δ ++ Γ)
  quoteᴬ Δ i k = reifyᴬ i (reify vl^𝓥ᴬ Δ i k)

  field

    _>>ᴿ_  :  𝓔ᴿ Γ Δ ρᴬ ρᴮ ρᴬᴮ  All 𝓥ᴿ Θ vsᴮ vsᴬᴮ 
              let id>>ρᴬ = freshˡ vl^𝓥ᴬ Δ >> th^Env 𝓢ᴬ.th^𝓥 ρᴬ (freshʳ vl^Var Θ)
              in 𝓔ᴿ (Θ ++ Γ) (Θ ++ Δ) id>>ρᴬ (vsᴮ >> ρᴮ) (vsᴬᴮ >> ρᴬᴮ)

    th^𝓔ᴿ  : 𝓔ᴿ Γ Δ ρᴬ ρᴮ ρᴬᴮ   (ρ : Thinning Θ Ω) 
             𝓔ᴿ Γ Δ ρᴬ (th^Env 𝓢ᴮ.th^𝓥 ρᴮ ρ) (th^Env 𝓢ᴬᴮ.th^𝓥 ρᴬᴮ ρ)

  𝓡 :   σ  (Γ ─Env) 𝓥ᴬ Δ  (Δ ─Env) 𝓥ᴮ Θ  (Γ ─Env) 𝓥ᴬᴮ Θ 
       Tm d s σ Γ  Set
  𝓡 σ ρᴬ ρᴮ ρᴬᴮ t = rel 𝓒ᴿ σ (evalᴮ ρᴮ (reifyᴬ σ (evalᴬ ρᴬ t))) (evalᴬᴮ ρᴬᴮ t)

  field

    varᴿ : 𝓔ᴿ Γ Δ ρᴬ ρᴮ ρᴬᴮ   v  𝓡 σ ρᴬ ρᴮ ρᴬᴮ (`var v)

    algᴿ : 𝓔ᴿ Γ Δ ρᴬ ρᴮ ρᴬᴮ  (b :  d  (Scope (Tm d s)) σ Γ) 
           let  bᴬ :   d  (Kripke 𝓥ᴬ 𝓒ᴬ) _ _
                bᴬ   = fmap d (𝓢ᴬ.body ρᴬ) b
                bᴮ   = fmap d  Δ i  𝓢ᴮ.body ρᴮ Δ i  quoteᴬ Δ i) bᴬ
                bᴬᴮ  = fmap d (𝓢ᴬᴮ.body ρᴬᴮ) b
           in  d ⟧ᴿ (Kripkeᴿ 𝓥ᴿ 𝓒ᴿ) bᴮ bᴬᴮ  𝓡 σ ρᴬ ρᴮ ρᴬᴮ (`con b)

  fusion : 𝓔ᴿ Γ Δ ρᴬ ρᴮ ρᴬᴮ  (t : Tm d s σ Γ)  𝓡 σ ρᴬ ρᴮ ρᴬᴮ t

  body   : 𝓔ᴿ Γ Δ ρᴬ ρᴮ ρᴬᴮ   Δ σ  (b : Scope (Tm d s) Δ σ Γ) 
           let vᴮ   = 𝓢ᴮ.body ρᴮ Δ σ (quoteᴬ Δ σ (𝓢ᴬ.body ρᴬ Δ σ b))
               vᴬᴮ  = 𝓢ᴬᴮ.body ρᴬᴮ Δ σ b
           in Kripkeᴿ 𝓥ᴿ 𝓒ᴿ Δ σ vᴮ vᴬᴮ

  fusion ρᴿ (`var v) = varᴿ ρᴿ v
  fusion ρᴿ (`con t) = algᴿ ρᴿ t (rew (liftᴿ d (body ρᴿ) t)) where

     eq  = fmap² d (𝓢ᴬ.body _)  Δ i t  𝓢ᴮ.body _ Δ i (quoteᴬ Δ i t)) t
     rew = subst  v   d ⟧ᴿ (Kripkeᴿ 𝓥ᴿ 𝓒ᴿ) v _) (sym eq)

  body ρᴿ []       i b = fusion ρᴿ b
  body ρᴿ (σ  Δ)  i b = λ ρ vsᴿ  fusion (th^𝓔ᴿ ρᴿ ρ >>ᴿ vsᴿ) b