{-# OPTIONS --without-K #-}
open import Agda.Primitive using (Level; lzero; lsuc; _⊔_)
open import Data.List using (List; []; _∷_)
open import Data.Nat using (; zero; suc)
open import Data.Product using (_×_) renaming (_,_ to ⟨_,_⟩ )
open import Structures
open import GenericSubstitution
open import Function using (_∘_)
import Relation.Binary.PropositionalEquality as Eq
open Eq using (_≡_; refl; sym; trans; cong; cong₂; cong-app; subst)
open Eq.≡-Reasoning
open import Sig
open import Var

module MapFusion (Op : Set) (sig : Op  List Sig) where

open import AbstractBindingTree Op sig
open import Map Op sig
open import GSubst
open import Renaming 
open Renaming.WithOpSig Op sig

record QuoteShift {}(V : Set ) {{S : Shiftable V}} 
  : Set  where
  field {{V-is-Quotable}} : Quotable V
  field quote-var→val :  x   (var→val{}{V} x)   ` x
        quote-shift :  (v : V)    v   rename ( 1)  v 

open QuoteShift {{...}} public

instance
  Var-is-QuoteShift : QuoteShift Var
  Var-is-QuoteShift = record { quote-var→val = λ x  refl
                             ; quote-shift = λ v  refl }
  ABT-is-QuoteShift : QuoteShift ABT
  ABT-is-QuoteShift = record { quote-var→val = λ x  refl
                             ; quote-shift = λ v  refl }

map-rename-fusion : ∀{}{V₂ V₃ : Set }
  {{_ : Shiftable V₂}} {{_ : Shiftable V₃}}
  {{_ : QuoteShift V₂}}{{_ : QuoteShift V₃}}
  {ρ₁ : Rename}{σ₂ : GSubst V₂}{σ₃ : GSubst V₃}
    (M : ABT)
    σ₂  ρ₁  σ₃
    map σ₂ (rename ρ₁ M)  map σ₃ M
map-rename-fusion {}{V₂}{V₃}{ρ₁ = ρ₁}{σ₂}{σ₃} M σ₂○ρ₁≈σ₃ =
  map-map-fusion-ext{lzero}{}{}{Var}{V₂}{V₃}{σ₁ = ρ₁}{σ₂}{σ₃}
            M σ₂○ρ₁≈σ₃ map-ext
  where
  map-ext : ∀{ρ₁ : Rename}{σ₂ : GSubst V₂}{σ₃ : GSubst V₃}
           σ₂  ρ₁  σ₃  ext σ₂  ext ρ₁  ext σ₃
  map-ext {ρ₁} {σ₂} {σ₃} σ₂○ρ₁≈σ₃ zero =
      trans (quote-var→val{}{V₂} 0) (sym (quote-var→val{}{V₃} 0))
  map-ext {ρ₁} {σ₂} {σ₃} σ₂○ρ₁≈σ₃ (suc x)=
          trans (quote-shift{}{V₂} (σ₂ (ρ₁ x)))
                (sym (trans (quote-shift{}{V₃} _)
                            (cong (rename ( 1)) (sym (σ₂○ρ₁≈σ₃ x)))))

rename-map-fusion : ∀{}{V₁ V₃ : Set }
  {{_ : Shiftable V₁}} {{_ : Shiftable V₃}}
  {{_ : QuoteShift V₁}}{{_ : QuoteShift V₃}}
  {σ₁ : GSubst V₁}{ρ₂ : Rename}{σ₃ : GSubst V₃}
    (M : ABT)
    ρ₂  σ₁  σ₃
    rename ρ₂ (map σ₁ M)  map σ₃ M
rename-map-fusion {}{V₁}{V₃}{σ₁ = σ₁}{ρ₂}{σ₃} M ρ₂○σ₁≈σ₃ =
  map-map-fusion-ext{}{lzero}{}{V₁}{Var}{V₃}{σ₁ = σ₁}{ρ₂}{σ₃}
            M ρ₂○σ₁≈σ₃ map-ext 
  where
  map-ext : ∀{σ₁ : GSubst V₁}{ρ₂ : Rename}{σ₃ : GSubst V₃}
           ρ₂  σ₁  σ₃  ext ρ₂  ext σ₁  ext σ₃
  map-ext {σ₁} {ρ₂} {σ₃} ρ₂○σ₁≈σ₃ zero rewrite quote-var→val{}{V₁} 0
    | quote-var→val{}{V₃} 0 = refl
  map-ext {σ₁} {ρ₂} {σ₃} ρ₂○σ₁≈σ₃ (suc x) rewrite quote-shift{}{V₁} (σ₁ x)
      | quote-shift{}{V₃} (σ₃ x) = begin
      rename (0   ρ₂) (map ( 1)  σ₁ x )
          ≡⟨ compose-rename ( 1) (0   ρ₂)  σ₁ x  
      rename ( 1  (0   ρ₂))  σ₁ x 
         ≡⟨ cong    rename   σ₁ x ) (ren-tail ( ρ₂) 0) 
      rename ( ρ₂)  σ₁ x 
         ≡⟨ cong    rename   σ₁ x ) (inc=⨟ᵣ↑ ρ₂) 
      rename (ρ₂   1)  σ₁ x 
         ≡⟨ sym (compose-rename ρ₂ ( 1)  σ₁ x ) 
      rename ( 1) (rename ρ₂  σ₁ x )
         ≡⟨ cong (rename ( 1)) (ρ₂○σ₁≈σ₃ x) 
      map ( 1)  σ₃ x 
      

map-map-fusion : ∀{}{V₁ V₂ V₃ : Set }
  {{_ : Shiftable V₁}} {{_ : Shiftable V₂}} {{_ : Shiftable V₃}}
  {{_ : QuoteShift V₁}}{{_ : QuoteShift V₂}}{{_ : QuoteShift V₃}}
  {σ₁ : GSubst V₁}{σ₂ : GSubst V₂}{σ₃ : GSubst V₃}
    (M : ABT)
    σ₂  σ₁  σ₃
    map σ₂ (map σ₁ M)  map σ₃ M
map-map-fusion {}{V₁}{V₂}{V₃} M σ₂○σ₁≈σ₃ =
  map-map-fusion-ext M σ₂○σ₁≈σ₃ mm-fuse-ext
  where
  G : ∀{σ₂ : GSubst V₂}  _○_≈_ {lzero}{}{}{Var}
                         ((var→val 0)   σ₂) ( 1) ( σ₂)
  G {σ₂} x = refl
  H : ∀{σ₂ : GSubst V₂}   1  σ₂   σ₂
  H {σ₂} x rewrite quote-shift{}{V₂} (σ₂ x) = refl

  mm-fuse-ext : ∀{σ₁ : GSubst V₁}{σ₂ : GSubst V₂}{σ₃ : GSubst V₃}
       σ₂  σ₁  σ₃  ext σ₂  ext σ₁  ext σ₃
  mm-fuse-ext {σ₁}{σ₂}{σ₃} σ₂○σ₁≈σ₃ zero =
      begin
          map ((var→val 0)   σ₂)  (var→val{}{V₁} 0) 
              ≡⟨ cong (map ((var→val{}{V₂} 0)   σ₂)) (quote-var→val{}{V₁} 0) 
          map ((var→val 0)   σ₂) (` 0)       ≡⟨⟩
           ((var→val 0)   σ₂) 0 
                                        ≡⟨⟩
           (var→val{}{V₂} 0)         ≡⟨ (quote-var→val{}{V₂} 0) 
          ` 0                        ≡⟨ sym (quote-var→val{}{V₃} 0) 
           (var→val{}{V₃} 0)         
  mm-fuse-ext {σ₁}{σ₂}{σ₃} σ₂○σ₁≈σ₃ (suc x) = begin
      map ((var→val 0)   σ₂)   (σ₁ x) 
              ≡⟨ cong (map ((var→val 0)   σ₂)) (quote-shift{}{V₁} (σ₁ x)) 
      map ((var→val 0)   σ₂) (rename ( 1)  σ₁ x )
                     ≡⟨ map-rename-fusion  σ₁ x  G 
      map ( σ₂)  σ₁ x 
                              ≡⟨ sym (rename-map-fusion  σ₁ x  H) 
      rename ( 1) (map σ₂  σ₁ x ) ≡⟨ cong (rename ( 1)) (σ₂○σ₁≈σ₃ x) 
      rename ( 1)  σ₃ x  ≡⟨ sym (quote-shift{}{V₃} (σ₃ x)) 
        (σ₃ x)