{-# OPTIONS --without-K --rewriting #-}

open import lib.Basics
open import lib.types.Pi
open import lib.types.TwoSemiCategory
open import lib.two-semi-categories.Functor

module lib.two-semi-categories.FunCategory where

module _ {i j k} (A : Type i) (G : TwoSemiCategory j k) where

  private
    module G = TwoSemiCategory G

  fun-El : Type (lmax i j)
  fun-El = A  G.El

  fun-Arr : fun-El  fun-El  Type (lmax i k)
  fun-Arr F G =  a  G.Arr (F a) (G a)

  fun-Arr-level : (F G : fun-El)  has-level 1 (fun-Arr F G)
  fun-Arr-level F G = Π-level  a  G.Arr-level (F a) (G a))

  fun-comp : {F G H : fun-El} (α : fun-Arr F G) (β : fun-Arr G H)
     fun-Arr F H
  fun-comp α β a = G.comp (α a) (β a)

  fun-assoc : {F G H I : fun-El} (α : fun-Arr F G) (β : fun-Arr G H) (γ : fun-Arr H I)
     fun-comp (fun-comp α β) γ == fun-comp α (fun-comp β γ)
  fun-assoc α β γ = λ=  a  G.assoc (α a) (β a) (γ a))

  abstract
    fun-pentagon : {F G H I J : fun-El}
      (α : fun-Arr F G) (β : fun-Arr G H) (γ : fun-Arr H I) (δ : fun-Arr I J)
       fun-assoc (fun-comp α β) γ δ ◃∙
        fun-assoc α β (fun-comp γ δ) ◃∎
        =ₛ
        ap  s  fun-comp s δ) (fun-assoc α β γ) ◃∙
        fun-assoc α (fun-comp β γ) δ ◃∙
        ap (fun-comp α) (fun-assoc β γ δ) ◃∎
    fun-pentagon α β γ δ =
      fun-assoc (fun-comp α β) γ δ ◃∙
      fun-assoc α β (fun-comp γ δ) ◃∎
        =ₛ⟨ ∙-λ=  a  G.assoc (G.comp (α a) (β a)) (γ a) (δ a))
                  a  G.assoc (α a) (β a) (G.comp (γ a) (δ a))) 
      λ=  a  G.assoc (G.comp (α a) (β a)) (γ a) (δ a) 
                G.assoc (α a) (β a) (G.comp (γ a) (δ a))) ◃∎
        =ₛ₁⟨ ap λ= (λ=  a  =ₛ-out (G.pentagon-identity (α a) (β a) (γ a) (δ a)))) 
      λ=  a  ap  s  G.comp s (δ a)) (G.assoc (α a) (β a) (γ a)) 
                G.assoc (α a) (G.comp (β a) (γ a)) (δ a) 
                ap (G.comp (α a)) (G.assoc (β a) (γ a) (δ a))) ◃∎
        =ₛ⟨ λ=-∙∙  a  ap  s  G.comp s (δ a)) (G.assoc (α a) (β a) (γ a)))
                   a  G.assoc (α a) (G.comp (β a) (γ a)) (δ a))
                   a  ap (G.comp (α a)) (G.assoc (β a) (γ a) (δ a))) 
      λ=  a  ap  s  G.comp s (δ a)) (G.assoc (α a) (β a) (γ a))) ◃∙
      fun-assoc α (fun-comp β γ) δ ◃∙
      λ=  a  ap (G.comp (α a)) (G.assoc (β a) (γ a) (δ a))) ◃∎
        =ₛ₁⟨ 0 & 1 & ! (λ=-ap  a s  G.comp s (δ a))  a  G.assoc (α a) (β a) (γ a))) 
      ap  s  fun-comp s δ) (fun-assoc α β γ) ◃∙
      fun-assoc α (fun-comp β γ) δ ◃∙
      λ=  a  ap (G.comp (α a)) (G.assoc (β a) (γ a) (δ a))) ◃∎
        =ₛ₁⟨ 2 & 1 & ! (λ=-ap  a  G.comp (α a))  a  G.assoc (β a) (γ a) (δ a))) 
      ap  s  fun-comp s δ) (fun-assoc α β γ) ◃∙
      fun-assoc α (fun-comp β γ) δ ◃∙
      ap (fun-comp α) (fun-assoc β γ δ) ◃∎ ∎ₛ

  fun-cat : TwoSemiCategory (lmax i j) (lmax i k)
  fun-cat =
    record
    { El = fun-El
    ; Arr = fun-Arr
    ; Arr-level = fun-Arr-level
    ; two-semi-cat-struct =
      record
      { comp = fun-comp
      ; assoc = fun-assoc
      ; pentagon-identity = fun-pentagon
      }
    }

module _ {i j₁ k₁ j₂ k₂} (A : Type i) {G : TwoSemiCategory j₁ k₁} {H : TwoSemiCategory j₂ k₂}
  (F : TwoSemiFunctor G H) where

  private
    module G = TwoSemiCategory G
    module H = TwoSemiCategory H
    module F = TwoSemiFunctor F
    module fun-G = TwoSemiCategory (fun-cat A G)
    module fun-H = TwoSemiCategory (fun-cat A H)

  fun-F₀ : fun-G.El  fun-H.El
  fun-F₀ I = λ a  F.F₀ (I a)

  fun-F₁ : {I J : fun-G.El}  fun-G.Arr I J  fun-H.Arr (fun-F₀ I) (fun-F₀ J)
  fun-F₁ α = λ a  F.F₁ (α a)

  fun-pres-comp : {I J K : fun-G.El} (α : fun-G.Arr I J) (β : fun-G.Arr J K)
     fun-F₁ (fun-G.comp α β) == fun-H.comp (fun-F₁ α) (fun-F₁ β)
  fun-pres-comp α β = λ=  a  F.pres-comp (α a) (β a))

  abstract
    fun-pres-comp-coh : {I J K L : fun-G.El}
      (α : fun-G.Arr I J) (β : fun-G.Arr J K) (γ : fun-G.Arr K L)
       fun-pres-comp (fun-G.comp α β) γ ◃∙
        ap  s  fun-H.comp s (fun-F₁ γ)) (fun-pres-comp α β) ◃∙
        fun-H.assoc (fun-F₁ α) (fun-F₁ β) (fun-F₁ γ) ◃∎
        =ₛ
        ap fun-F₁ (fun-G.assoc α β γ) ◃∙
        fun-pres-comp α (fun-G.comp β γ) ◃∙
        ap (fun-H.comp (fun-F₁ α)) (fun-pres-comp β γ) ◃∎
    fun-pres-comp-coh α β γ =
      fun-pres-comp (fun-G.comp α β) γ ◃∙
      ap  s  fun-H.comp s (fun-F₁ γ)) (fun-pres-comp α β) ◃∙
      fun-H.assoc (fun-F₁ α) (fun-F₁ β) (fun-F₁ γ) ◃∎
        =ₛ₁⟨ 1 & 1 & λ=-ap  a s  H.comp s (fun-F₁ γ a) )  a  F.pres-comp (α a) (β a)) 
      fun-pres-comp (fun-G.comp α β) γ ◃∙
      λ=  a  ap  s  H.comp s (fun-F₁ γ a)) (F.pres-comp (α a) (β a))) ◃∙
      fun-H.assoc (fun-F₁ α) (fun-F₁ β) (fun-F₁ γ) ◃∎
        =ₛ⟨ ∙∙-λ=  a  F.pres-comp (G.comp (α a) (β a)) (γ a))
                   a  ap  s  H.comp s (fun-F₁ γ a)) (F.pres-comp (α a) (β a)))
                   a  H.assoc (fun-F₁ α a) (fun-F₁ β a) (fun-F₁ γ a)) 
      λ=  a  F.pres-comp (G.comp (α a) (β a)) (γ a) 
                ap  s  H.comp s (fun-F₁ γ a)) (F.pres-comp (α a) (β a)) 
                H.assoc (fun-F₁ α a) (fun-F₁ β a) (fun-F₁ γ a)) ◃∎
        =ₛ₁⟨ ap λ= (λ=  a  =ₛ-out (F.pres-comp-coh (α a) (β a) (γ a)))) 
      λ=  a  ap F.F₁ (G.assoc (α a) (β a) (γ a)) 
                F.pres-comp (α a) (G.comp (β a) (γ a)) 
                ap (H.comp (fun-F₁ α a)) (F.pres-comp (β a) (γ a))) ◃∎
        =ₛ⟨ λ=-∙∙  a  ap F.F₁ (G.assoc (α a) (β a) (γ a)))
                   a  F.pres-comp (α a) (G.comp (β a) (γ a)))
                   a  ap (H.comp (fun-F₁ α a)) (F.pres-comp (β a) (γ a))) 
      λ=  a  ap F.F₁ (G.assoc (α a) (β a) (γ a))) ◃∙
      fun-pres-comp α (fun-G.comp β γ) ◃∙
      λ=  a  ap (H.comp (fun-F₁ α a)) (F.pres-comp (β a) (γ a))) ◃∎
        =ₛ₁⟨ 0 & 1 & ! $ λ=-ap  _  F.F₁)  a  G.assoc (α a) (β a) (γ a)) 
      ap fun-F₁ (fun-G.assoc α β γ) ◃∙
      fun-pres-comp α (fun-G.comp β γ) ◃∙
      λ=  a  ap (H.comp (fun-F₁ α a)) (F.pres-comp (β a) (γ a))) ◃∎
        =ₛ₁⟨ 2 & 1 & ! $ λ=-ap  a  H.comp (fun-F₁ α a))  a  F.pres-comp (β a) (γ a)) 
      ap fun-F₁ (fun-G.assoc α β γ) ◃∙
      fun-pres-comp α (fun-G.comp β γ) ◃∙
      ap (fun-H.comp (fun-F₁ α)) (fun-pres-comp β γ) ◃∎ ∎ₛ

  fun-functor-map : TwoSemiFunctor (fun-cat A G) (fun-cat A H)
  fun-functor-map =
    record
    { F₀ = fun-F₀
    ; F₁ = fun-F₁
    ; pres-comp = fun-pres-comp
    ; pres-comp-coh = fun-pres-comp-coh
    }