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

open import Categories.Category

--  obtain free properties from duality
module Categories.Diagram.Pushout.Properties {o  e} (C : Category o  e) where

open Category C

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

open import Categories.Category.Cocartesian C using (Cocartesian)
open import Categories.Morphism C using (_≅_; Epi; Iso)
open import Categories.Morphism.Properties C using (Iso-swap)
open import Categories.Morphism.Duality C using (op-≅⇒≅; Iso⇒op-Iso)
open import Categories.Object.Initial C using (Initial)
open import Categories.Object.Terminal op using (Terminal)
open import Categories.Object.Coproduct C using (Coproduct)
open import Categories.Object.Duality C
open import Categories.Diagram.Coequalizer C using (Coequalizer)
open import Categories.Diagram.Pushout C using (Pushout)
open import Categories.Diagram.Duality C
open import Categories.Diagram.Pullback op as P′ using (Pullback)
open import Categories.Diagram.Pullback.Properties op

private
  variable
    A B X Y : Obj
    f g h i : A  B

module _ (p : Pushout f g) where
  open Pushout p

  private
    pullback : Pullback f g
    pullback = Pushout⇒coPullback p
  
  open Pullback pullback
    using (unique′; id-unique; unique-diagram)
    public

  up-to-iso : (p′ : Pushout f g)  Pushout.Q p  Pushout.Q p′
  up-to-iso p′ = op-≅⇒≅ (P′.up-to-iso (Pushout⇒coPullback p) (Pushout⇒coPullback p′))
  
  swap : Pushout g f
  swap = coPullback⇒Pushout (P′.swap pullback)
  
  glue : Pushout h i₁  Pushout (h  f) g
  glue p = coPullback⇒Pushout (P′.glue pullback (Pushout⇒coPullback p))
  
  unglue : Pushout (h  f) g  Pushout h i₁
  unglue p = coPullback⇒Pushout (P′.unglue pullback (Pushout⇒coPullback p))

  Pushout-resp-Epi : Epi g  Epi i₁
  Pushout-resp-Epi epi = P′.Pullback-resp-Mono pullback epi

  Pushout-resp-Iso : Iso g h   λ j  Iso i₁ j
  Pushout-resp-Iso iso with P′.Pullback-resp-Iso pullback (Iso⇒op-Iso (Iso-swap iso))
  ... | j , record { isoˡ = isoˡ ; isoʳ = isoʳ } = j , record { isoˡ = isoʳ ; isoʳ = isoˡ }

Coproduct×Coequalizer⇒Pushout : (cp : Coproduct A B) 
                                Coequalizer (Coproduct.i₁ cp  f) (Coproduct.i₂ cp  g) 
                                Pushout f g
Coproduct×Coequalizer⇒Pushout cp coe =
  coPullback⇒Pushout (P′.Product×Equalizer⇒Pullback (Coproduct⇒coProduct cp)
                                                    (Coequalizer⇒coEqualizer coe))

Coproduct×Pushout⇒Coequalizer : (cp : Coproduct A B) 
                                Pushout f g 
                                Coequalizer (Coproduct.i₁ cp  f) (Coproduct.i₂ cp  g)
Coproduct×Pushout⇒Coequalizer cp p =
  coEqualizer⇒Coequalizer (P′.Product×Pullback⇒Equalizer (Coproduct⇒coProduct cp)
                                                         (Pushout⇒coPullback p))

module _ (i : Initial) where
  open Initial i
  private
    t : Terminal
    t = ⊥⇒op⊤ i

  pushout-⊥⇒coproduct : Pushout (! {X}) (! {Y})  Coproduct X Y
  pushout-⊥⇒coproduct p = coProduct⇒Coproduct (pullback-⊤⇒product t (Pushout⇒coPullback p))

  coproduct⇒pushout-⊥ : Coproduct X Y  Pushout (! {X}) (! {Y})
  coproduct⇒pushout-⊥ c = coPullback⇒Pushout (product⇒pullback-⊤ t (Coproduct⇒coProduct c))

pushout-resp-≈ : Pushout f g  f  h  g  i  Pushout h i
pushout-resp-≈ p eq eq′ = coPullback⇒Pushout (pullback-resp-≈ (Pushout⇒coPullback p) eq eq′)

module _ (pushouts :  {X Y Z} (f : X  Y) (g : X  Z)  Pushout f g)
         (cocartesian : Cocartesian) where
  open Cocartesian cocartesian
  open Dual

  pushout×cocartesian⇒coequalizer : Coequalizer f g
  pushout×cocartesian⇒coequalizer = coEqualizer⇒Coequalizer
    (pullback×cartesian⇒equalizer  f g  Pushout⇒coPullback (pushouts f g)) op-cartesian)