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

open import Categories.Category.Core

module Categories.Object.Product.Limit {o  e} (C : Category o  e) where

open import Level
open import Data.Nat.Base using ()
open import Data.Fin.Base using (Fin)
open import Data.Fin.Patterns

open import Categories.Category.Lift
open import Categories.Category.Finite.Fin
open import Categories.Category.Finite.Fin.Construction.Discrete
open import Categories.Object.Product C
open import Categories.Diagram.Limit
open import Categories.Functor.Core

import Categories.Category.Construction.Cones as Co
import Categories.Morphism.Reasoning as MR

private
  module C = Category C
  open C
  open MR C
  open HomReasoning

module _ {o′ ℓ′ e′} {F : Functor (liftC o′ ℓ′ e′ (Discrete 2)) C} where
  private
    module F = Functor F
    open F

  limit⇒product : Limit F  Product (F₀ (lift 0F)) (F₀ (lift 1F))
  limit⇒product L = record
    { A×B      = apex
    ; π₁       = proj (lift 0F)
    ; π₂       = proj (lift 1F)
    ; ⟨_,_⟩    = λ f g  rep record
      { apex = record
        { ψ       = λ { (lift 0F)  f
                      ; (lift 1F)  g }
        ; commute = λ { {lift 0F} {lift 0F} (lift 0F)  elimˡ identity
                      ; {lift 1F} {lift 1F} (lift 0F)  elimˡ identity }
        }
      }
    ; project₁ = commute
    ; project₂ = commute
    ; unique   = λ {_} {h} eq eq′  terminal.!-unique record
      { arr     = h
      ; commute = λ { {lift 0F}  eq
                    ; {lift 1F}  eq′ }
      }
    }
    where open Limit L

module _ o′ ℓ′ e′ A B where
  open Equiv

  product⇒limit-F : Functor (liftC o′ ℓ′ e′ (Discrete 2)) C
  product⇒limit-F = record
    { F₀           = λ { (lift 0F)  A
                       ; (lift 1F)  B }
    ; F₁           = λ { {lift 0F} {lift 0F} _  C.id
                       ; {lift 1F} {lift 1F} _  C.id }
    ; identity     = λ { {lift 0F}  refl
                       ; {lift 1F}  refl }
    ; homomorphism = λ { {lift 0F} {lift 0F} {lift 0F}  sym identity²
                       ; {lift 1F} {lift 1F} {lift 1F}  sym identity² }
    ; F-resp-≈     = λ { {lift 0F} {lift 0F} _  refl
                       ; {lift 1F} {lift 1F} _  refl }
    }

module _ o′ ℓ′ e′ {A B} (p : Product A B) where
  open Product p
  private
    F = product⇒limit-F o′ ℓ′ e′ A B
    open Functor F

  product⇒limit : Limit F
  product⇒limit = record
    { terminal = record
      {         = record
        { N    = A×B
        ; apex = record
          { ψ       = λ { (lift 0F)  π₁
                        ; (lift 1F)  π₂ }
          ; commute = λ { {lift 0F} {lift 0F} (lift 0F)  identityˡ
                        ; {lift 1F} {lift 1F} (lift 0F)  identityˡ }
          }
        }
      ; ⊤-is-terminal = record
        { !        = λ {K} 
          let open Co.Cone F K
          in record
          { arr     =  ψ (lift 0F) , ψ (lift 1F) 
          ; commute = λ { {lift 0F}  project₁
                        ; {lift 1F}  project₂ }
          }
        ; !-unique = λ {K} f 
          let module K = Co.Cone F K
              module f = Co.Cone⇒ F f
          in begin
             K.ψ (lift 0F) , K.ψ (lift 1F)  ≈˘⟨ ⟨⟩-cong₂ f.commute f.commute 
             π₁  f.arr , π₂  f.arr        ≈⟨ g-η 
            f.arr                             
        }
      }
    }