{-# OPTIONS --without-K --safe #-}

-- Some of the obvious properties of cartesian functors
module Categories.Functor.Cartesian.Properties where

open import Level
open import Data.Product using (Σ ; _,_)

open import Categories.Category.BinaryProducts using (BinaryProducts)
open import Categories.Category.Core using (Category)
open import Categories.Category.Cartesian.Bundle using (CartesianCategory)
open import Categories.Category.Monoidal.Bundle using (MonoidalCategory)
open import Categories.Functor using (Functor; _∘F_) renaming (id to idF)
open import Categories.Functor.Properties
open import Categories.Functor.Cartesian using (CartesianF; IsCartesianF)
open import Categories.Functor.Monoidal
open import Categories.NaturalTransformation using (NaturalTransformation; ntHelper)

import Categories.Morphism as M
import Categories.Morphism.Reasoning as MR
import Categories.Object.Terminal as 
import Categories.Object.Product as P

private
  variable
    o  e o′ ℓ′ e′ o″ ℓ″ e″ : Level

module _ (C : CartesianCategory o  e) where
  open CartesianCategory C using (terminal; U; products)
  open BinaryProducts products using (product)
  open ⊤.Terminal terminal
  open P U

  idF-IsCartesianF : IsCartesianF C C idF
  idF-IsCartesianF = record
    { F-resp-⊤ = ⊤-is-terminal
    ; F-resp-× = Product⇒IsProduct product
    }

  idF-CartesianF : CartesianF C C
  idF-CartesianF = record
    { isCartesian = idF-IsCartesianF
    }

module _ (A : CartesianCategory o  e) (B : CartesianCategory o′ ℓ′ e′) (C : CartesianCategory o″ ℓ″ e″) where
  private
    module A = CartesianCategory A
    module B = CartesianCategory B
    module C = CartesianCategory C
    module AP = BinaryProducts A.products using (π₁; π₂)
    module BP = BinaryProducts B.products using (π₁; π₂; project₁; project₂)
    module CP = BinaryProducts C.products using (⟨_,_⟩; project₁; project₂; unique′)
    open P C.U

  ∘-IsCartesianF :  {F : Functor A.U B.U} {G : Functor B.U C.U} 
                    IsCartesianF B C G  IsCartesianF A B F 
                    IsCartesianF A C (G ∘F F)
  ∘-IsCartesianF {F} {G} CG CF = record
    { F-resp-⊤ = ⊤.Terminal.⊤-is-terminal (⊤.transport-by-iso C.U C.terminal
                                          (M.≅.trans C.U (M.≅.sym C.U CG.⊤-iso) ([ G ]-resp-≅ (M.≅.sym B.U CF.⊤-iso))))
    ; F-resp-× = record
      { ⟨_,_⟩    = λ f g  G.₁ (CF.×-iso.to _ _) C.∘ CG.×-iso.to _ _ C.∘ CP.⟨ f , g 
      ; project₁ = λ {_ f g}  begin
        G.₁ (F.₁ AP.π₁) C.∘ G.₁ (CF.×-iso.to _ _) C.∘ CG.×-iso.to _ _ C.∘ CP.⟨ f , g  ≈⟨ refl⟩∘⟨ refl⟩∘⟨ ([ CG.F-prod _ _ ]⟨⟩∘  CG.F-prod.⟨⟩-cong₂ _ _ CP.project₁ CP.project₂) 
        G.₁ (F.₁ AP.π₁) C.∘ G.₁ (CF.×-iso.to _ _) C.∘ CG.F-resp-×.⟨ f , g             ≈⟨ pullˡ ([ G ]-resp-∘ CF.F-resp-×.project₁) 
        G.₁ BP.π₁ C.∘ CG.F-resp-×.⟨ f , g                                             ≈⟨ CG.F-resp-×.project₁ 
        f 
      ; project₂ = λ {_ f g}  begin
        G.₁ (F.₁ AP.π₂) C.∘ G.₁ (CF.×-iso.to _ _) C.∘ CG.×-iso.to _ _ C.∘ CP.⟨ f , g  ≈⟨ refl⟩∘⟨ refl⟩∘⟨ ([ CG.F-prod _ _ ]⟨⟩∘  CG.F-prod.⟨⟩-cong₂ _ _ CP.project₁ CP.project₂) 
        G.₁ (F.₁ AP.π₂) C.∘ G.₁ (CF.×-iso.to _ _) C.∘ CG.F-resp-×.⟨ f , g             ≈⟨ pullˡ ([ G ]-resp-∘ CF.F-resp-×.project₂) 
        G.₁ BP.π₂ C.∘ CG.F-resp-×.⟨ f , g                                             ≈⟨ CG.F-resp-×.project₂ 
        g 
      ; unique   = λ {_ h f g} eq₁ eq₂  begin
        G.₁ (CF.×-iso.to _ _) C.∘ CG.×-iso.to _ _ C.∘ CP.⟨ f , g 
          ≈⟨ refl⟩∘⟨ refl⟩∘⟨ CP.unique′ (CP.project₁   eq₁  pushˡ ( ([ G ]-resp-∘ BP.project₁))  pushˡ ( CP.project₁))
                                       (CP.project₂   eq₂  pushˡ ( ([ G ]-resp-∘ BP.project₂))  pushˡ ( CP.project₂)) 
        G.₁ (CF.×-iso.to _ _) C.∘ CG.×-iso.to _ _ C.∘ CG.×-iso.from _ _ C.∘ G.₁ (CF.×-iso.from _ _) C.∘ h
          ≈⟨ refl⟩∘⟨ cancelˡ (CG.×-iso.isoˡ _ _) 
        G.₁ (CF.×-iso.to _ _) C.∘ G.₁ (CF.×-iso.from _ _) C.∘ h
          ≈⟨ cancelˡ (M._≅_.isoˡ ([ G ]-resp-≅ (CF.×-iso _ _))) 
        h
          
      }
    }
    where module F  = Functor F
          module G  = Functor G
          module CG = IsCartesianF CG
          module CF = IsCartesianF CF
          open C.HomReasoning
          open MR C.U

∘-CartesianF : {A : CartesianCategory o  e} {B : CartesianCategory o′ ℓ′ e′} {C : CartesianCategory o″ ℓ″ e″} 
               CartesianF B C  CartesianF A B  CartesianF A C
∘-CartesianF G F = record { isCartesian = ∘-IsCartesianF _ _ _ (CartesianF.isCartesian G) (CartesianF.isCartesian F) }

module _ {C : CartesianCategory o  e} {D : CartesianCategory o′ ℓ′ e′} where
  private
    module C  = CartesianCategory C
    module CP = BinaryProducts C.products
    module CM = MonoidalCategory C.monoidalCategory
    module D  = CartesianCategory D
    module DP = BinaryProducts D.products
    module DM = MonoidalCategory D.monoidalCategory using (associator)
    open D.HomReasoning
    open MR D.U
    open M D.U

  isMonoidalFunctor : CartesianF C D  MonoidalFunctor C.monoidalCategory D.monoidalCategory
  isMonoidalFunctor F = record
    { F          = F.F
    ; isMonoidal = record
      { ε             = F.⊤-iso.to
      ; ⊗-homo        = ntHelper record
        { η       = λ { (X , Y)  F.×-iso.to X Y }
        ; commute = λ { {X , Y} {Z , W} (f , g) 
          F.F-prod.unique′ _ _
            (begin
              F.₁ CP.π₁ D.∘ F.×-iso.to Z W D.∘ (F.₁ f DP.⁂ F.₁ g) ≈⟨ pullˡ F.F-resp-×.project₁ 
              DP.π₁ D.∘ (F.₁ f DP.⁂ F.₁ g)                        ≈⟨ DP.project₁ 
              F.₁ f D.∘ DP.π₁                                     ≈˘⟨ pullʳ F.F-resp-×.project₁ 
              (F.₁ f D.∘ F.₁ CP.π₁) D.∘ F.×-iso.to X Y            ≈˘⟨ pullˡ ([ F.F ]-resp-square CP.project₁) 
              F.₁ CP.π₁ D.∘ F.₁ (f CP.⁂ g) D.∘ F.×-iso.to X Y     )
            (begin
              F.₁ CP.π₂ D.∘ F.×-iso.to Z W D.∘ (F.₁ f DP.⁂ F.₁ g) ≈⟨ pullˡ F.F-resp-×.project₂ 
              DP.π₂ D.∘ (F.₁ f DP.⁂ F.₁ g)                        ≈⟨ DP.project₂ 
              F.₁ g D.∘ DP.π₂                                    ≈˘⟨ pullʳ F.F-resp-×.project₂ 
              (F.₁ g D.∘ F.₁ CP.π₂) D.∘ F.×-iso.to X Y           ≈˘⟨ pullˡ ([ F.F ]-resp-square CP.project₂) 
              F.₁ CP.π₂ D.∘ F.₁ (f CP.⁂ g) D.∘ F.×-iso.to X Y     )
          }
        }
      ; associativity = λ {X Y Z}  let open P D.U in begin
        F.₁ CM.associator.from D.∘ F.×-iso.to (X CP.× Y) Z D.∘ (F.×-iso.to X Y DP.⁂ D.id)
          ≈⟨ F.F-resp-⟨⟩′ _ _ ⟩∘⟨ [ DP.product  DP.product  F.F-prod _ _ ]repack∘× 
        F.F-resp-×.⟨ F.₁ (CP.π₁ C.∘ CP.π₁) , F.₁ CP.⟨ CP.π₂ C.∘ CP.π₁ , CP.π₂   D.∘ F.F-resp-×.⟨ F.×-iso.to X Y D.∘ DP.π₁ , D.id D.∘ DP.π₂ 
          ≈⟨ F.F-prod.⟨⟩-cong₂ _ _ F.homomorphism (F.F-resp-⟨⟩′ _ _  F.F-prod.⟨⟩-cong₂ _ _ F.homomorphism D.Equiv.refl) ⟩∘⟨refl 
        F.F-resp-×.⟨ F.₁ CP.π₁ D.∘ F.₁ CP.π₁ , F.F-resp-×.⟨ F.₁ CP.π₂ D.∘ F.₁ CP.π₁ , F.₁ CP.π₂   D.∘ F.F-resp-×.⟨ F.×-iso.to X Y D.∘ DP.π₁ , D.id D.∘ DP.π₂ 
          ≈⟨ [ F.F-prod _ _ ]⟨⟩∘ 
        F.F-resp-×.⟨ (F.₁ CP.π₁ D.∘ F.₁ CP.π₁) D.∘ _ , F.F-resp-×.⟨ F.₁ CP.π₂ D.∘ F.₁ CP.π₁ , F.₁ CP.π₂  D.∘ _ 
          ≈⟨ F.F-prod.⟨⟩-cong₂ _ _ (pullʳ F.F-resp-×.project₁  pullˡ F.F-resp-×.project₁)
                                   [ F.F-prod _ _ ]⟨⟩∘ 
        F.F-resp-×.⟨ DP.π₁ D.∘ DP.π₁ , F.F-resp-×.⟨ (F.₁ CP.π₂ D.∘ F.₁ CP.π₁) D.∘ _ , F.₁ CP.π₂ D.∘ _  
          ≈⟨ F.F-prod.⟨⟩-cong₂ _ _ D.Equiv.refl
            (F.F-prod.⟨⟩-cong₂ _ _ (pullʳ F.F-resp-×.project₁  pullˡ F.F-resp-×.project₂)
                                   (F.F-resp-×.project₂  D.identityˡ)) 
        F.F-resp-×.⟨ DP.π₁ D.∘ DP.π₁ , F.F-resp-×.⟨ DP.π₂ D.∘ DP.π₁ , DP.π₂  
          ≈˘⟨ F.F-prod.⟨⟩-cong₂ _ _ D.identityˡ ([ F.F-prod _ _ ]⟨⟩∘  (F.F-prod.⟨⟩-cong₂ _ _ DP.project₁ DP.project₂)) 
        F.F-resp-×.⟨ D.id D.∘ DP.π₁ D.∘ DP.π₁ , F.×-iso.to Y Z D.∘ DP.⟨ DP.π₂ D.∘ DP.π₁ , DP.π₂  
          ≈˘⟨ [ DP.product  (F.F-prod _ _) ]×∘⟨⟩ 
        F.F-resp-×.⟨ D.id D.∘ DP.π₁ , F.×-iso.to Y Z D.∘ DP.π₂  D.∘ DM.associator.from
          ≈˘⟨ pullˡ [ DP.product  DP.product  F.F-prod _ _ ]repack∘× 
        F.×-iso.to X (Y CP.× Z) D.∘ (D.id DP.⁂ F.×-iso.to Y Z) D.∘ DM.associator.from
          
      ; unitaryˡ      = begin
        F.₁ CP.π₂ D.∘ F.F-resp-×.⟨ DP.π₁ , DP.π₂  D.∘ (F.F-resp-⊤.! DP.⁂ D.id) ≈⟨ pullˡ F.F-resp-×.project₂ 
        DP.π₂ D.∘ (F.F-resp-⊤.! DP.⁂ D.id)                                     ≈⟨ DP.project₂ 
        D.id D.∘ DP.π₂                                                         ≈⟨ D.identityˡ 
        DP.π₂                                                                  
      ; unitaryʳ      = begin
        F.₁ CP.π₁ D.∘ F.F-resp-×.⟨ DP.π₁ , DP.π₂  D.∘ (D.id DP.⁂ F.F-resp-⊤.!) ≈⟨ pullˡ F.F-resp-×.project₁ 
        DP.π₁ D.∘ (D.id DP.⁂ F.F-resp-⊤.!)                                     ≈⟨ DP.project₁ 
        D.id D.∘ DP.π₁                                                         ≈⟨ D.identityˡ 
        DP.π₁                                                                  
      }
    }
    where module F = CartesianF F

  isStrongMonoidalFunctor : CartesianF C D  StrongMonoidalFunctor C.monoidalCategory D.monoidalCategory
  isStrongMonoidalFunctor F = record
    { F                = F.F
    ; isStrongMonoidal = record
      { ε             = ≅.sym F.⊤-iso
      ; ⊗-homo        = record
        { F⇒G = MF.⊗-homo
        ; F⇐G = ntHelper record
          { η       = λ { (X , Y)  F.×-iso.from X Y }
          ; commute = λ { {X , Y} {Z , W} (f , g) 
            begin
              DP.⟨ F.₁ CP.π₁ , F.₁ CP.π₂  D.∘ F.₁ (f CP.⁂ g)                    ≈⟨ DP.⟨⟩∘ 
              DP.⟨ F.₁ CP.π₁ D.∘ F.₁ (f CP.⁂ g) , F.₁ CP.π₂ D.∘ F.₁ (f CP.⁂ g)  ≈⟨ DP.⟨⟩-cong₂ ([ F.F ]-resp-square CP.project₁) ([ F.F ]-resp-square CP.project₂) 
              DP.⟨ F.₁ f D.∘ F.F₁ CP.π₁ , F.₁ g D.∘ F.F₁ CP.π₂                  ≈˘⟨ DP.⁂∘⟨⟩ 
              (F.₁ f DP.⁂ F.₁ g) D.∘ DP.⟨ F.F₁ CP.π₁ , F.F₁ CP.π₂                }
          }
        ; iso = λ { (X , Y)  record
          { isoˡ = F.×-iso.isoʳ X Y
          ; isoʳ = F.×-iso.isoˡ X Y
          } }
        }
      ; associativity = MF.associativity
      ; unitaryˡ      = MF.unitaryˡ
      ; unitaryʳ      = MF.unitaryʳ
      }
    }
    where module F  = CartesianF F
          module MF = MonoidalFunctor (isMonoidalFunctor F)