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

module Categories.Functor.Profunctor.Cograph where

open import Level
open import Data.Empty.Polymorphic using ()
open import Data.Product using (_,_; _×_)
open import Data.Sum using (_⊎_; inj₁; inj₂)
open import Function.Bundles using (Func; _⟨$⟩_)
open Func using (cong)
open import Relation.Binary using (Setoid; module Setoid)

open import Categories.Category using (Category; _[_,_]; _[_∘_]; _[_≈_])
open import Categories.Functor using (Functor)
open import Categories.Functor.Bifunctor.Properties using ([_]-commute)
open import Categories.Functor.Profunctor using (Profunctor)

module _ {o  e o′ ℓ′ e′ ℓ″ e″}
    {C : Category o  e} {D : Category o′ ℓ′ e′} (P : Profunctor ℓ″ e″ C D)
    where
  open Profunctor P

  cograph : Category _ _ _
  cograph = record
    { Obj = D.Obj  C.Obj
    ; _⇒_ = λ
      { (inj₁ d) (inj₁ d′)  Lift (  ℓ″) (D [ d , d′ ])
      ; (inj₁ d) (inj₂ c)  Lift (  ℓ′) (Setoid.Carrier (F₀ (d , c)))
      ; (inj₂ c) (inj₁ d)  
      ; (inj₂ c) (inj₂ c′)  Lift (ℓ′  ℓ″) (C [ c , c′ ])
      }
    ; _≈_ = λ
      { {inj₁ d} {inj₁ d′} f g  Lift (e  e″) (D [ lower f  lower g ])
      ; {inj₁ d} {inj₂ c} p q  Lift (e  e′) (Setoid._≈_ (F₀ (d , c)) (lower p) (lower q))
      ; {inj₂ c} {inj₁ d} () ()
      ; {inj₂ c} {inj₂ c′} f g  Lift (e′  e″) (C [ lower f  lower g ])
      }
    ; id = λ
      { {inj₁ d}  lift D.id
      ; {inj₂ c}  lift C.id
      }
    ; _∘_ = λ
      { {inj₁ dA} {inj₁ dB} {inj₁ dC} f g  lift (D [ lower f  lower g ])
      ; {inj₁ dA} {inj₁ dB} {inj₂ cC} p g  lift (₁ˡ (lower g) ⟨$⟩ lower p)
      ; {inj₁ dA} {inj₂ cB} {inj₂ cC} f q  lift (₁ʳ (lower f) ⟨$⟩ lower q)
      ; {inj₂ cA} {inj₂ cB} {inj₂ cC} f g  lift (C [ lower f  lower g ])
      }
    ; assoc = λ
      { {inj₁ dA} {inj₁ dB} {inj₁ dC} {inj₁ dD} {f} {g} {h}  lift D.assoc
      ; {inj₁ dA} {inj₁ dB} {inj₁ dC} {inj₂ cD} {f} {g} {r} 
          lift (Setoid.sym (F₀ _) (homomorphismˡ))
      ; {inj₁ dA} {inj₁ dB} {inj₂ cC} {inj₂ cD} {f} {q} {h} 
          lift (Setoid.sym (F₀ _) ([ bimodule ]-commute))
      ; {inj₁ dA} {inj₂ cB} {inj₂ cC} {inj₂ cD} {p} {g} {h} 
          lift (homomorphismʳ )
      ; {inj₂ cA} {inj₂ cB} {inj₂ cC} {inj₂ cD} {f} {g} {h}  lift C.assoc
      }
    ; sym-assoc = λ
      { {inj₁ dA} {inj₁ dB} {inj₁ dC} {inj₁ dD} {f} {g} {h}  lift D.sym-assoc
      ; {inj₁ dA} {inj₁ dB} {inj₁ dC} {inj₂ cD} {f} {g} {r}  lift homomorphismˡ
      ; {inj₁ dA} {inj₁ dB} {inj₂ cC} {inj₂ cD} {f} {q} {h}  lift ([ bimodule ]-commute )
      ; {inj₁ dA} {inj₂ cB} {inj₂ cC} {inj₂ cD} {p} {g} {h} 
          lift (Setoid.sym (F₀ _) homomorphismʳ)
      ; {inj₂ cA} {inj₂ cB} {inj₂ cC} {inj₂ cD} {f} {g} {h}  lift C.sym-assoc
      }
    ; identityˡ = λ
      { {inj₁ dA} {inj₁ dB} {f}  lift D.identityˡ
      ; {inj₁ dA} {inj₂ cB} {p}  lift identity
      ; {inj₂ cA} {inj₂ cB} {f}  lift C.identityˡ
      }
    ; identityʳ = λ
      { {inj₁ dA} {inj₁ dB} {f}  lift D.identityʳ
      ; {inj₁ dA} {inj₂ cB} {p}  lift identity
      ; {inj₂ cA} {inj₂ cB} {f}  lift C.identityʳ
      }
    ; identity² = λ
      { {inj₁ d}  lift D.identity²
      ; {inj₂ c}  lift C.identity²
      }
    ; equiv = λ
      { {inj₁ dA} {inj₁ dB}  record
          { refl = lift D.Equiv.refl
          ; sym = λ x  lift (D.Equiv.sym (lower x))
          ; trans = λ x y  lift (D.Equiv.trans (lower x) (lower y))
          }
      ; {inj₁ dA} {inj₂ cB}  record
          { refl = lift (Setoid.refl (F₀ _))
          ; sym = λ x  lift (Setoid.sym (F₀ _) (lower x))
          ; trans = λ x y  lift (Setoid.trans (F₀ _) (lower x) (lower y))
          }
      ; {inj₂ cA} {inj₁ dB}  record
          { refl = λ {}
          ; sym = λ {}
          ; trans = λ {}
          }
      ; {inj₂ cA} {inj₂ cB}  record
          { refl = lift C.Equiv.refl
          ; sym = λ x  lift (C.Equiv.sym (lower x))
          ; trans = λ x y  lift (C.Equiv.trans (lower x) (lower y))
          }
      }
    ; ∘-resp-≈ = λ
      { {inj₁ dA} {inj₁ dB} {inj₁ dC} {f} {h} {g} {i} 
          λ x y  lift (D.∘-resp-≈ (lower x) (lower y))
      ; {inj₁ dA} {inj₁ dB} {inj₂ cC} {f} {h} {g} {i} 
          λ f≈h g≈i  lift (
          Setoid.trans (F₀ _) (resp-≈ˡ (lower g≈i))
                              (cong (F₁ _) (lower f≈h)))
      ; {inj₁ dA} {inj₂ cB} {inj₂ cC} {f} {h} {g} {i} 
          λ f≈h g≈i  lift (
          Setoid.trans (F₀ _) (resp-≈ʳ (lower f≈h)) (cong (F₁ _) (lower g≈i)))
      ; {inj₂ cA} {inj₂ cB} {inj₂ cC} {f} {h} {g} {i} 
          λ x y  lift (C.∘-resp-≈ (lower x) (lower y))
      }
    }
    where
    module C = Category C
    module D = Category D

  module cograph where
    open Category cograph public

    Inj₁ : Functor D cograph
    Inj₁ = record
      { F₀ = inj₁
      ; F₁ = lift
      ; identity = lift (Category.Equiv.refl D)
      ; homomorphism = lift (Category.Equiv.refl D)
      ; F-resp-≈ = lift
      }
    module Inj₁ = Functor Inj₁

    Inj₂ : Functor C cograph
    Inj₂ = record
      { F₀ = inj₂
      ; F₁ = lift
      ; identity = lift (Category.Equiv.refl C)
      ; homomorphism = lift (Category.Equiv.refl C)
      ; F-resp-≈ = lift
      }
    module Inj₂ = Functor Inj₂