{-# OPTIONS --without-K --hidden-argument-puns --safe --lossy-unification #-}
open import Prelude

module IL.Monoidal  {o  e} {C : Category o  e} (MC : Monoidal C) where


open Monoidal MC using (; _⊗₀_; _⊗₁_)
open import Categories.NaturalTransformation.NaturalIsomorphism hiding (_≃_)

open import IL.Core (MC) renaming (id to idIL) using (IL; FILM⟨_,_,_⟩; _⇒ᶠⁱˡ_)
open import fil (MC) using (FIL; FIL[_,_,_])
open import Data.Product using (uncurry; uncurry′)

private
  module C = Category C
  module  = Category (C ×ᶜ C)

module MC = Monoidal MC

module _ where
  open FIL
  unit : FIL
  unit .F = idF
  unit .G = idF
  -- agda doesn't like `idN` here, instead we need the unitor
  unit .ϕ  = unitorʳ.F⇒G
    where module unitorʳ = NaturalIsomorphism unitorʳ


infixr 10 _⊗L₀_
_⊗L₀_ : FIL  FIL  FIL
(FIL[ F , _ , _ ] ⊗L₀ FIL[ J , _ , _ ]) .FIL.F = F ∘F J
(FIL[ _ , G , _ ] ⊗L₀ FIL[ _ , K , _ ]) .FIL.G = G ∘F K
(FIL[ F , G , ϕ ] ⊗L₀ FIL[ J , K , ψ ]) .FIL.ϕ  = replaceˡ (ψ ∘ᵥ ϕ ∘ʳ (J  K)) (associator (J  K) (F  G) )

module _ {A B D : Category o  e} {F G H : Functor A B} {I J K : Functor B D}
    {α : NaturalTransformation F G} {β : NaturalTransformation G H}
    {δ : NaturalTransformation I J} {γ : NaturalTransformation J K} where
  private
    module α = NaturalTransformation α
    module β = NaturalTransformation β
    module δ = NaturalTransformation δ
    module γ = NaturalTransformation γ
    module D = Category D
    module K = Functor K
  --open MR D
  open Category.HomReasoning D
  open Functor F using (F₀)
  open Functor G using () renaming (F₀ to G₀)
  open Functor H using () renaming (F₀ to H₀)
  open Functor J using () renaming (F₁ to J₁)
  open Functor K using () renaming (F₁ to K₁)
  ≃-interchange : (γ ∘ᵥ δ) ∘ₕ (β ∘ᵥ α)  (γ ∘ₕ β) ∘ᵥ (δ ∘ₕ α)
  ≃-interchange {x} = begin
      NaturalTransformation.η ((γ ∘ᵥ δ) ∘ₕ β ∘ᵥ α) x
      ≈⟨ D.Equiv.refl 
      D [ K₁ (B [ β.η x  α.η x ])  D [ γ.η (F₀ x)  δ.η (F₀ x)] ]
      ≈⟨ K.homomorphism ⟩∘⟨refl 
      D [ D [ K₁ (β.η     x)  K₁ (α.η x) ]   D [ γ.η (F₀ x)  δ.η (F₀ x)] ]
      ≈⟨ D.assoc  refl⟩∘⟨ D.sym-assoc 
      D [ K₁ (β.η x)          D [ D [ K₁ (α.η x)  γ.η (F₀ x) ]  δ.η (F₀ x)] ]
      ≈⟨ refl⟩∘⟨ γ.sym-commute (α.η x) ⟩∘⟨refl 
      D [ K₁ (β.η x)          D [ D [ γ.η (G₀ x)  J₁ (α.η x) ]  δ.η (F₀ x)] ]
      ≈˘⟨ D.assoc  refl⟩∘⟨ D.sym-assoc 
      D [     D [ K₁ (β.η x)  γ.η (G₀ x) ]   D [ J₁ (α.η x)  δ.η (F₀ x)] ]
      ≈⟨ D.Equiv.refl 
      NaturalTransformation.η ((γ ∘ₕ β) ∘ᵥ δ ∘ₕ α) x 

module _ where
  open import Categories.Category.Monoidal.Reasoning (MC)
  infixr 10 _⊗L₁_

  _⊗L₁_ : {L L' M M' : FIL} 
          (L ⇒ᶠⁱˡ L')  (M ⇒ᶠⁱˡ M') 
          IL [ L ⊗L₀ M , L' ⊗L₀ M' ]
  (FILM⟨ f , _ , _  ⊗L₁ FILM⟨ j , _ , _ ) ._⇒ᶠⁱˡ_.f = f ∘ₕ j
  (FILM⟨ _ , g , _  ⊗L₁ FILM⟨ _ , k , _ ) ._⇒ᶠⁱˡ_.g = g ∘ₕ k
  _⊗L₁_ {L} {L'} {M} {M'} FILM⟨ f , g , isMap₁  FILM⟨ j , k , isMap₂  ._⇒ᶠⁱˡ_.isMap {(x , y)} = begin
      (_ ∘ᵥ  ∘ˡ (idN ⁂ⁿ g ∘ₕ k)) .η (x , y)
      ≈⟨ Equiv.refl 
      ((ψ .η (x , y)  ϕ .η (J₀ x ,  K₀ y))  idC)  (idC ⊗₁ (G₁ (k .η y)  g .η (K'₀ y)))
      ≈⟨ pushˡ C.identityʳ 
      ψ .η  (x , y)  ϕ .η (J₀ x  , K₀  y)          (idC ⊗₁ (G₁ (k .η y)  g .η (K'₀ y)))
      ≈⟨ refl⟩∘⟨ refl⟩∘⟨ split₂ʳ  -- slide down g
      ψ .η (x , y)  ϕ .η (J₀ x  , K₀  y)          (idC ⊗₁ G₁ (k .η y))
                                                        (idC ⊗₁ g .η (K'₀ y))
      ≈⟨ refl⟩∘⟨ refl⟩∘⟨  (Functor.identity F) ⟩⊗⟨refl ⟩∘⟨refl
        refl⟩∘⟨ extendʳ (NaturalTransformation.commute ϕ _)
        -- slide up k
      ψ .η (x , y)  (idC ⊗₁ (k .η y))   ϕ .η (J₀ x  , K'₀  y)
                                              (idC ⊗₁ g .η (K'₀ y))
      ≈⟨ extendʳ isMap₂ 
      ψ' .η (x , y)  (j .η x ⊗₁ idC)   ϕ .η (J₀ x  , K'₀  y)
                                            (idC ⊗₁ g .η (K'₀ y))
      ≈⟨ refl⟩∘⟨ refl⟩∘⟨ isMap₁ 
      ψ' .η (x , y)  (j .η x ⊗₁ idC)   ϕ' .η (J₀ x  , K'₀  y)
                                            (f .η (J₀ x) ⊗₁ idC)
      ≈⟨ refl⟩∘⟨ extendʳ (NaturalTransformation.sym-commute ϕ' _) 
        refl⟩∘⟨ refl⟩∘⟨ refl⟩⊗⟨ G'.identity ⟩∘⟨refl  -- slide down j
      ψ' .η (x , y)  ϕ' .η (J'₀ x , K'₀ y)  (F'₁ (j .η x) ⊗₁ idC)
                                                 (f .η (J₀ x)  ⊗₁ idC)
      ≈˘⟨ refl⟩∘⟨ refl⟩∘⟨ split₁ʳ  -- slide up f
      ψ' .η (x , y)  ϕ' .η (J'₀ x , K'₀ y)  (F'₁ (j .η x)  f .η (J₀ x)) ⊗₁ idC
      ≈˘⟨ pushˡ C.identityʳ 
      ((ψ' .η (x , y)  ϕ' .η (J'₀ x , K'₀ y))  idC)  (F'₁ (j .η x)  f .η (J₀ x)) ⊗₁ idC
      ≈⟨ Equiv.refl 
      (_ ∘ᵥ  ∘ˡ (f ∘ₕ j ⁂ⁿ idN)) .η (x , y)
      
    where open FIL L  using (ϕ; F; G)
          open NaturalTransformation using (η)
          open C renaming (id to idC)
          open MR C
          open FIL L' renaming (ϕ to ϕ'; F to F'; G to G')
          open FIL M  renaming (ϕ to ψ; F to J; G to K)
          open FIL M' renaming (ϕ to ψ'; F to J'; G to K')
          open Functor F' using () renaming (F₀ to F'₀; F₁ to F'₁)
          open Functor G  using () renaming (F₀ to G₀; F₁ to G₁)
          open G' using () renaming (F₀ to G'₀; F₁ to G'₁)
          open Functor J  using () renaming (F₀ to J₀; F₁ to J₁)
          open Functor J' using () renaming (F₀ to J'₀; F₁ to J'₁)
          open Functor K  using () renaming (F₀ to K₀; F₁ to K₁)
          open Functor K' using () renaming (F₀ to K'₀; F₁ to K'₁)

private module _ {F : Endofunctor C} where
  open Functor F
  open Category C
  open MR C
  open import Categories.Category.Monoidal.Reasoning (MC)
  f-eq : {A : Obj}  F₁ {A} id  id  id
  f-eq = elimˡ identity

⊗-IL : Bifunctor IL IL IL
⊗-IL = record
  { F₀           = uncurry _⊗L₀_
  ; F₁           = uncurry _⊗L₁_
  ; identity     = λ {(FIL[ F , G , _ ] , FIL[ J , K , _ ])}   {x}  f-eq {F = F} {A = Functor.F₀ J x}) , λ {x}  f-eq {F = G} {A = Functor.F₀ K x}
  ; homomorphism = λ {_} {_} {_} {(FILM⟨ f , g , _  , FILM⟨ j , k , _ )} {(FILM⟨ f' , g' , _   , FILM⟨ j' , k' , _ )}
                      ≃-interchange {α = j} {β = j'} {δ = f} {γ = f'}  , ≃-interchange {α = k'} {β = k} {δ = g'} {γ = g}
  ; F-resp-≈     = λ { {A = (FIL[ F , G , _ ] , FIL[ F' , G' , _ ])} {B = (FIL[ M , N , _ ] , FIL[ M' , N' , _ ] )} {f = (f₁ , f₂)} {g = (g₁ , g₂)} ((e₁₁ , e₁₂) , (e₂₁ , e₂₂))
                      (Functor.F-resp-≈ M e₂₁ ⟩∘⟨ e₁₁) , (Functor.F-resp-≈ G e₂₂ ⟩∘⟨ e₁₂) }
  }
  where open Category C
        open HomReasoning

module _ where
  open import Categories.Morphism IL using (_≅_; Iso)
  open Category C
  open MR C
  open import Categories.Category.Monoidal.Reasoning (MC)
  open NaturalTransformation using (η)
  NatIso⇒ILIso :  {L M : FIL}
            (let open FIL L)
            (let open FIL M renaming (ϕ to ψ; F to F'; G to G'))
            (F≃F' : F ≃ⁿ F')
            (G≃G' : G' ≃ⁿ G)
            (let open NaturalIsomorphism F≃F'  renaming (F⇒G to F⇒F';F⇐G to F⇐F'; module iso to iso₁))
            (let open NaturalIsomorphism G≃G'  renaming (F⇒G to G'⇒G;F⇐G to G'⇐G; module iso to iso₂))
            (isMap₁ : (ϕ ∘ᵥ  ∘ˡ (idN ⁂ⁿ G'⇒G))  (ψ ∘ᵥ  ∘ˡ (F⇒F' ⁂ⁿ idN)))
            --(isMap₂ : (ψ  ∘ᵥ ⊗ ∘ˡ (idN ⁂ⁿ G'⇐G)) ≃ (ϕ ∘ᵥ ⊗ ∘ˡ (F⇐F' ⁂ⁿ idN)))
           L   M
  NatIso⇒ILIso {L} {M} F≃F' G≃G' isMap₁ = record
    { from = record
      { f     = F⇒F'
      ; g     = G'⇒G
      ; isMap = isMap₁
      }
    ; to = record
      { f     = F⇐F'
      ; g     = G'⇐G
      ; isMap = isMap₂
      }
    ; iso = record
      { isoˡ = F≃F'.iso.isoˡ _ , G≃G'.iso.isoʳ _
      ; isoʳ = F≃F'.iso.isoʳ _ , G≃G'.iso.isoˡ _
      }
    }
    where open FIL L
          open FIL M renaming (ϕ to ψ; F to F'; G to G')
          open NaturalIsomorphism F≃F' renaming (F⇒G to F⇒F';F⇐G to F⇐F'; module iso to iso₁)
          open NaturalIsomorphism G≃G' renaming (F⇒G to G'⇒G;F⇐G to G'⇐G; module iso to iso₂)
          module F≃F' = NaturalIsomorphism F≃F'
          module G≃G' = NaturalIsomorphism G≃G'
          isMap₂ : (ψ  ∘ᵥ  ∘ˡ (idN ⁂ⁿ G'⇐G))  (ϕ ∘ᵥ  ∘ˡ (F⇐F' ⁂ⁿ idN))
          isMap₂ {(x , y)} = begin
            (ψ ∘ᵥ  ∘ˡ (idN ⁂ⁿ G'⇐G)) .η (x , y)
            ≈⟨ refl⟩∘⟨ Equiv.sym (F≃F'.iso.isoʳ _) ⟩⊗⟨refl 
            (ψ ∘ᵥ  ∘ˡ ((F⇒F' ∘ᵥ F⇐F') ⁂ⁿ G'⇐G) ) .η (x , y)
            ≈⟨ refl⟩∘⟨ refl⟩⊗⟨  identityˡ 
            (ψ ∘ᵥ  ∘ˡ (F⇒F' ⁂ⁿ idN)  ∘ᵥ (F⇐F' ⁂ⁿ G'⇐G)) .η (x , y)
            ≈⟨ refl⟩∘⟨ ⊗-distrib-over-∘
              extendʳ ( isMap₁)
              refl⟩∘⟨  ⊗-distrib-over-∘  -- isMap₁
            (ϕ ∘ᵥ  ∘ˡ (idN ⁂ⁿ G'⇒G) ∘ᵥ (F⇐F' ⁂ⁿ G'⇐G)) .η (x , y)
            ≈⟨ refl⟩∘⟨ identityˡ ⟩⊗⟨refl 
            (ϕ  ∘ᵥ  ∘ˡ (F⇐F'  ⁂ⁿ (G'⇒G ∘ᵥ G'⇐G))) .η (x , y)
            ≈⟨ refl⟩∘⟨ refl⟩⊗⟨ G≃G'.iso.isoʳ _ 
            (ϕ  ∘ᵥ  ∘ˡ (F⇐F'  ⁂ⁿ idN)) .η (x , y)
            

  unitorˡ-IL : {X : FIL}  unit ⊗L₀ X  X
  unitorˡ-IL {X} = NatIso⇒ILIso unitorˡ (sym unitorˡ) λ {x}  begin
      ((ϕ .η x  id)  id)  (id ⊗₁ id)
      ≈⟨ (identityʳ  identityʳ) ⟩∘⟨refl 
      ϕ .η x  (id ⊗₁ id)
      
    where open FIL X

  unitorʳ-IL : {X : FIL}  X ⊗L₀ unit  X
  unitorʳ-IL {X} = NatIso⇒ILIso unitorʳ (sym unitorʳ) λ {x}  begin
      (((id  ϕ .η x))  id)  (id ⊗₁ id)
      ≈⟨ (identityʳ  identityˡ) ⟩∘⟨refl 
      ϕ .η x  (id ⊗₁ id)
      
    where open FIL X

  associator-IL : {X Y Z : FIL}  (X ⊗L₀ Y) ⊗L₀ Z  X ⊗L₀ (Y ⊗L₀ Z)
  associator-IL {X} {Y} {Z} = NatIso⇒ILIso (associator _ _ _) (sym-associator _ _ _) λ {(x , y)}  begin
      ((Χ .η (x , y)  (ψ .η (F''₀ x , G''₀ y)  ϕ .η (F'₀ (F''₀ x) , G'₀ (G''₀ y)))  id)  id)  (id ⊗₁ id)
      ≈⟨ (identityʳ  refl⟩∘⟨ identityʳ) ⟩∘⟨refl 
      (Χ .η (x , y)  ψ .η (F''₀ x , G''₀ y)  ϕ .η (F'₀ (F''₀ x) , G'₀ (G''₀ y)))  (id ⊗₁ id)
      ≈⟨ Equiv.sym identityʳ ⟩∘⟨refl
        sym-assoc ⟩∘⟨refl ⟩∘⟨refl
        (refl⟩∘⟨  identityˡ) ⟩∘⟨refl ⟩∘⟨refl
        sym-assoc ⟩∘⟨refl ⟩∘⟨refl 
      ((((Χ .η (x , y)  ψ .η (F''₀ x , G''₀ y))  id)  ϕ .η (F'₀ (F''₀ x) , G'₀ (G''₀ y)))  id)  (id ⊗₁ id)
      
    where open FIL X
          open FIL Y renaming (ϕ to ψ; F to F'; G to G')
          open FIL Z renaming (ϕ to Χ; F to F''; G to G'')
          open Functor F using (F₀; F₁)
          open Functor F' using () renaming (F₀ to F'₀; F₁ to F'₁)
          open Functor G' using () renaming (F₀ to G'₀; F₁ to G'₁)
          open Functor F'' using () renaming (F₀ to F''₀; F₁ to F''₁)
          open Functor G'' using () renaming (F₀ to G''₀; F₁ to G''₁)

  open Definitions IL
  module unitorˡ-IL {X} = _≅_ (unitorˡ-IL {X = X})
  module unitorʳ-IL {X} = _≅_ (unitorʳ-IL {X = X})
  module associator-IL {X} {Y} {Z} = _≅_ (associator-IL {X} {Y} {Z})

  unitorˡ-commute : ∀{L M} {f : L ⇒ᶠⁱˡ M} 
                    CommutativeSquare (idIL ⊗L₁ f) unitorˡ-IL.from unitorˡ-IL.from f
  unitorˡ-commute = identityˡ , (identityʳ  identityʳ   identityˡ)

  unitorʳ-commute : ∀{L M} {f : L ⇒ᶠⁱˡ M} 
                    CommutativeSquare (f ⊗L₁ idIL) unitorʳ-IL.from unitorʳ-IL.from f
  unitorʳ-commute {L} {M} {f = fil-morphism} =  {x}  begin
      id  J.₁ id  f .η x
      ≈⟨ identityˡ 
      J.₁ id  f .η x
      ≈⟨ J.identity ⟩∘⟨refl  identityˡ   identityʳ 
      f .η x  id
      )  , λ {x}  begin
      (G.₁ id  g .η x)  id
      ≈⟨ identityʳ  G.identity ⟩∘⟨refl 
      id  g .η x
      
    where open _⇒ᶠⁱˡ_ fil-morphism
          open FIL L using (F; G)
          open FIL M using () renaming (F to J; G to K)
  assoc-commute : ∀{L M : Category.Obj IL}      {f : IL [ L   , M   ]}
                   {L' M' : Category.Obj IL}    {g : IL [ L'  , M'  ]}
                   {L'' M'' : Category.Obj IL}  {h : IL [ L'' , M'' ]} 
                 CommutativeSquare
                 ((f ⊗L₁ g) ⊗L₁ h) associator-IL.from
                 associator-IL.from (f ⊗L₁ (g ⊗L₁ h))
  assoc-commute {L} {M} {f = f1} {L'} {M'} {g = f2} {L''} {M''} {h = f3} =  {x}  begin
      id  J.₁ (J'.₁ (f'' .η x))  J.₁ (f' .η (F''.₀ x))  f .η (F'.₀ (F''.₀ x))
      ≈⟨ identityˡ 
      J.₁ (J'.₁ (f'' .η x))  J.₁ (f' .η (F''.₀ x))  f .η (F'.₀ (F''.₀ x))
      ≈⟨ pullˡ ( J.homomorphism) 
      J.₁ (J'.₁ (f'' .η x)  (f' .η (F''.₀ x)))  f .η (F'.₀ (F''.₀ x))
      ≈⟨  identityʳ 
      (J.₁ (J'.₁ (f'' .η x)  f' .η (F''.₀ x))  f .η (F'.₀ (F''.₀ x)))  id
      )  , λ {x}  begin
      (G.₁ (G'.₁ (g'' .η x))  G.₁ (g' .η (K''.₀ x))  g .η (K'.₀ (K''.₀ x)))  id
      ≈⟨ identityʳ 
      G.₁ (G'.₁ (g'' .η x))  G.₁ (g' .η (K''.₀ x))  g .η (K'.₀ (K''.₀ x))
      ≈⟨ pullˡ ( G.homomorphism) 
      G.₁ (G'.₁ (g'' .η x)  (g' .η (K''.₀ x)))  g .η (K'.₀ (K''.₀ x))
      ≈⟨  identityˡ 
      id  G.₁ (G'.₁ (g'' .η x)  g' .η (K''.₀ x))  g .η (K'.₀ (K''.₀ x))
      
    where open _⇒ᶠⁱˡ_ f1 using (f; g)
          open _⇒ᶠⁱˡ_ f2 using () renaming (f to f'; g to g')
          open _⇒ᶠⁱˡ_ f3 using () renaming (f to f''; g to g'')
          open FIL L using (F; G)
          open FIL M using () renaming (F to J; G to K)
          open FIL L' using () renaming (F to F'; G to G')
          open FIL M' using () renaming (F to J'; G to K')
          open FIL L'' using () renaming (F to F''; G to G'')
          open FIL M'' using () renaming (F to J''; G to K'')

  open Commutation IL

  triangle :  {X Y}  
             [ (X ⊗L₀ unit) ⊗L₀ Y  X ⊗L₀ Y ]⟨
               associator-IL.from ⇒⟨ X ⊗L₀ (unit ⊗L₀ Y) 
             idIL ⊗L₁ unitorˡ-IL.from
            unitorʳ-IL.from ⊗L₁ idIL
           
  triangle {X} {Y} = identityʳ , identityˡ
    where open FIL X using (F; G)
          open FIL Y using () renaming (F to J; G to K)

  pentagon :  {X Y Z W} 
             [ ((X ⊗L₀ Y) ⊗L₀ Z) ⊗L₀ W  X ⊗L₀ Y ⊗L₀ Z ⊗L₀ W ]⟨
               associator-IL.from ⊗L₁ idIL ⇒⟨ (X ⊗L₀ Y ⊗L₀ Z) ⊗L₀ W 
               associator-IL.from         ⇒⟨ X ⊗L₀ (Y ⊗L₀ Z) ⊗L₀ W 
               idIL ⊗L₁ associator-IL.from
              associator-IL.from         ⇒⟨ (X ⊗L₀ Y) ⊗L₀ Z ⊗L₀ W 
               associator-IL.from
             
  pentagon {X} {Y} {Z} =  {x}  begin
        (F.₁ id  id)  id  F.₁ (J.₁ (H.₁ id))  id
        ≈⟨ (identityʳ  F.identity) ⟩∘⟨ Equiv.refl ⟩∘⟨ (F.F-resp-≈ (J.F-resp-≈ H.identity  J.identity)  F.identity) ⟩∘⟨refl 
        id  id  id  id
        ≈⟨ identityˡ  identityˡ 
        id  id
        )  , λ {x}  begin
        ((G.₁ (K.₁ (I.₁ id))  id)  id)  G.₁ id  id
        ≈⟨ (identityʳ  identityʳ  G.F-resp-≈ (K.F-resp-≈ I.identity  K.identity)  G.identity) ⟩∘⟨refl 
        id  G.₁ id  id
        ≈⟨ identityˡ
          G.identity ⟩∘⟨refl 
        id  id
        
    where open FIL X using (F; G)
          open FIL Y using () renaming (F to J; G to K)
          open FIL Z using () renaming (F to H; G to I)

  IL-monoidal : Monoidal IL
  IL-monoidal = monoidalHelper IL record
    {                = ⊗-IL
    ; unit            = unit
    ; unitorˡ         = unitorˡ-IL
    ; unitorʳ         = unitorʳ-IL
    ; associator      = associator-IL
    ; unitorˡ-commute = unitorˡ-commute
    ; unitorʳ-commute = unitorʳ-commute
    ; assoc-commute   = assoc-commute
    ; triangle        = λ {X} {Y}  triangle {X} {Y}
    ; pentagon        = λ {X} {Y} {Z} {W}  pentagon {X} {Y} {Z} {W}
    }