{-# OPTIONS --safe #-}

module Data.Pushout where

open import Level
open import HLevels
open import Path
open import Function
open import Data.Sigma
open import Path.Reasoning

data SetPushout {A : Type a} {B : Type b} {C : Type c}
                (f : A  B) (g : A  C) : Type (a ℓ⊔ b ℓ⊔ c) where
  inl    : B  SetPushout f g
  inr    : C  SetPushout f g
  push   : (a : A)  inl (f a)  inr (g a)
  trunc : isSet (SetPushout f g)

module _
  {f : A  B} {g : A  C}
  (P : SetPushout f g  Type p)
  (prop : (x : SetPushout f g)  isProp (P x))
  (PB : (b : B)  P (inl b))
  (PC : (c : C)  P (inr c)) where

  elimProp : (x : SetPushout f g)  P x
  elimProp (inl x) = PB x
  elimProp (inr x) = PC x
  elimProp (push a i) = isOfHLevel→isOfHLevelDep 1 prop (PB (f a)) (PC (g a)) (push a) i
  elimProp (trunc xs ys p q i j) =
    isOfHLevel→isOfHLevelDep 2
       x  isProp→isSet (prop x))
      (elimProp xs) (elimProp ys)
      (cong elimProp p) (cong elimProp q)
      (trunc xs ys p q)
      i j

module _ {} {X : Type } {f : A  B} {g : A  C}
         (set : isSet X)
         (h₁ : B  X) (h₂ : C  X)
         (cancel : h₁  f  h₂  g)
         where
  rec : SetPushout f g  X
  rec (inl x) = h₁ x
  rec (inr x) = h₂ x
  rec (push a i) = cancel i a
  rec (trunc xs ys p q i j) =
    set (rec xs) (rec ys) (cong rec p) (cong rec q) i j

  ump : Σ![ u  (SetPushout f g  X) ] × (u  inl  h₁) × (u  inr  h₂)
  ump .fst = rec
  ump .snd .fst .fst = refl
  ump .snd .fst .snd = refl
  ump .snd .snd y (l , r) = 
    funExt (elimProp
              _
               _  set _ _)
               a  cong (_$ a) (sym l))
               c  cong (_$ c) (sym r)))