module GpdCont.QuotientContainer.Morphism where

open import GpdCont.Prelude
open import GpdCont.QuotientContainer.Base
open import GpdCont.QuotientContainer.Premorphism as Premorphism using (Premorphism ; PremorphismEquiv)

open import Cubical.Foundations.Equiv
open import Cubical.Foundations.HLevels
open import Cubical.HITs.PropositionalTruncation as PT using ()
open import Cubical.HITs.SetQuotients as SQ using (_/_)

private
  variable
     : Level
    Q R S T : QCont 

open QCont

Premorphism/ : (Q R : QCont ) (shape-mor : Q .Shape  R .Shape)  Type 
Premorphism/ Q R shape-mor = Premorphism Q R shape-mor / PremorphismEquiv

pre-morphism-class :  {u : Q .Shape  R .Shape} (f : Premorphism Q R u)  Premorphism/ Q R u
pre-morphism-class f = SQ.[ f ]

pre-morphism-eq/ :  {u : Q .Shape  R .Shape} {f g : Premorphism Q R u}  PremorphismEquiv f g  pre-morphism-class f  pre-morphism-class g
pre-morphism-eq/ {f} {g} f≈g = SQ.eq/ {R = PremorphismEquiv} f g f≈g

record Morphism {} (Q R : QCont ) : Type  where
  constructor mk-qcont-morphism
  pattern

  field
    shape-mor : Q .Shape  R .Shape
    pos-equiv : Premorphism/ Q R shape-mor

unquoteDecl MorphismIsoΣ = declareRecordIsoΣ MorphismIsoΣ (quote Morphism)

instance
  MorphismToΣ :  {} {Q R : QCont }  RecordToΣ (Morphism Q R)
  MorphismToΣ = toΣ MorphismIsoΣ

isSetMorphism : isSet (Morphism Q R)
isSetMorphism {Q} {R} = recordIsOfHLevel 2 $
  isSetΣ isSet-shape-mor isSet-Premorphism/
  where
  private
    module Q = QCont Q
    module R = QCont R
  
  isSet-shape-mor : isSet (Q.Shape  R.Shape)
  isSet-shape-mor = isSet→ (QCont.is-set-shape R)

  isSet-Premorphism/ :  shape-mor  isSet (Premorphism/ Q R shape-mor)
  isSet-Premorphism/ shape-mor = SQ.squash/

open Morphism

pre→mor :  {u : Q .Shape  R .Shape} (f : Premorphism Q R u)  Morphism Q R
pre→mor {u = u} f .shape-mor = u
pre→mor {u = u} f .pos-equiv = pre-morphism-class f

Morphism≡ :  {f g : Morphism Q R}
   (p : f .shape-mor  g .shape-mor)
   (q : PathP  i  Premorphism/ Q R (p i)) (f .pos-equiv) (g .pos-equiv))
   f  g
Morphism≡ p q i .shape-mor = p i
Morphism≡ p q i .pos-equiv = q i

PremorphismEquiv→Morphism≡ :  {u} {f g : Premorphism Q R u}  (PremorphismEquiv f g)  pre→mor f  pre→mor g
PremorphismEquiv→Morphism≡ r = Morphism≡ refl (pre-morphism-eq/ r)

opaque
  MorphismElimProp :  {ℓP} (P : Morphism Q S  Type ℓP)
     (∀ α  isProp (P α))
     ((u : Q .Shape  S .Shape) (f : Premorphism Q S u)  P (pre→mor f))
      α  P α
  MorphismElimProp P is-prop-P p* α = SQ.elimProp {P = λ f  P (mk-qcont-morphism (α .shape-mor) f)}
    (is-prop-P  (mk-qcont-morphism (α .shape-mor)))
    (p* (α .shape-mor))
    (α .pos-equiv)

  MorphismElimProp2 :  {Q Q′ R R′ : QCont } {ℓP} (P : Morphism Q R  Morphism Q′ R′  Type ℓP)
     (∀ α β  isProp (P α β))
     (∀ u v  (f : Premorphism Q R u) (g : Premorphism Q′ R′ v)  P (pre→mor f) (pre→mor g))
      α β  P α β
  MorphismElimProp2 P is-prop-P p* α β = SQ.elimProp2 {P = λ f g  P (mk-qcont-morphism _ f) (mk-qcont-morphism _ g)}
     f g  is-prop-P _  _)
    (p* _ _)
    (α .pos-equiv) (β .pos-equiv)

  MorphismElimProp3 :  {Q Q′ Q″ R R′ R″ : QCont } {ℓP} (P : Morphism Q R  Morphism Q′ R′  Morphism Q″ R″  Type ℓP)
     (∀ α β γ  isProp (P α β γ))
     (∀ u v w  (f : Premorphism Q R u) (g : Premorphism Q′ R′ v) (h : Premorphism Q″ R″ w)  P (pre→mor f) (pre→mor g) (pre→mor h))
      α β γ  P α β γ
  MorphismElimProp3 P is-prop-P p* α β γ = SQ.elimProp3 {P = λ f g h  P (mk-qcont-morphism _ f) (mk-qcont-morphism _ g) (mk-qcont-morphism _ h)}
     f g h  is-prop-P _ _ _)
    (p* _ _ _)
    (α .pos-equiv) (β .pos-equiv) (γ .pos-equiv)

MorphismRec :  {ℓP} {P : Type ℓP}
   isSet P
   (rec* : (u : Q .Shape  S .Shape) (f : Premorphism Q S u)  P)
   (well-defined :  {u} f f′  (PremorphismEquiv f f′)   rec* u f  rec* u f′)
   Morphism Q S  P
MorphismRec is-set-P rec* well-defined α = SQ.rec is-set-P (rec* (α .shape-mor)) well-defined (α .pos-equiv)

MorphismElim :  {ℓP} {P : Morphism Q S  Type ℓP}
   (∀ α  isSet (P α))
   (elim* : (u : Q .Shape  S .Shape) (f : Premorphism Q S u)  P (pre→mor f))
   (well-defined :  {u} f f′  (r : PremorphismEquiv f f′)  PathP  i  P (mk-qcont-morphism u (pre-morphism-eq/ r i))) (elim* u f) (elim* u f′))
    α   P α
MorphismElim {P} is-set-P elim* well-defined α =
  SQ.elim {P = λ f  P (mk-qcont-morphism (α .shape-mor) f)} (is-set-P  mk-qcont-morphism _) (elim* (α .shape-mor)) well-defined (α .pos-equiv)

opaque
  MorphismElimCompute :  {ℓP} {P : Morphism Q S  Type ℓP}
     (is-set-P :  α  isSet (P α))
     (elim* : (u : Q .Shape  S .Shape) (f : Premorphism Q S u)  P (pre→mor f))
     (well-defined :  {u} f f′  (r : PremorphismEquiv f f′)  PathP  i  P (mk-qcont-morphism u (pre-morphism-eq/ r i))) (elim* u f) (elim* u f′))
      (u : Q .Shape  S .Shape) (f : Premorphism Q S u)  MorphismElim is-set-P elim* well-defined (pre→mor f)  elim* u f
  MorphismElimCompute {P} is-set-P elim* well-defined u f = refl

idQCont : {Q : QCont }  Morphism Q Q
idQCont {Q} = pre→mor (Premorphism.idPremorphism Q)

compQContMorphism : (α : Morphism Q R) (β : Morphism R S)  Morphism Q S
compQContMorphism {Q} {R} {S} α β = composite where
  private
    module R = QCont R
    module S = QCont S

  open Premorphism.Composite {Q = Q} {R = R} {S = S} {u = α .shape-mor} {v = β .shape-mor} using () renaming (compPremorphism to _⋆-pre_)

  composite : Morphism Q S
  composite .shape-mor = α .shape-mor  β .shape-mor
  composite .pos-equiv = SQ.rec SQ.squash/ (composite' (β .pos-equiv)) (well-defined (β .pos-equiv)) (α .pos-equiv) where
    open import Cubical.HITs.PropositionalTruncation.Monad using (_>>=_ ; return)

    open Premorphism.Premorphism
    opaque
      well-defined' : (f : Premorphism Q R (α .shape-mor)) (g g′ : Premorphism R S (β .shape-mor))  PremorphismEquiv g g′  PremorphismEquiv (f ⋆-pre g) (f ⋆-pre g′)
      well-defined' f g g′ g-equiv-g′ s = do
        (σ , σ-comm)  (g-equiv-g′ (α .shape-mor s))
        return $ σ , cong (_⋆ f .pos-mor _) σ-comm

    composite' : Premorphism/ R S (β .shape-mor)  Premorphism Q R (α .shape-mor)  Premorphism/ Q S (α .shape-mor  β .shape-mor)
    composite' [g] f = SQ.rec SQ.squash/  g  pre-morphism-class (f ⋆-pre g))  { g g′ r  pre-morphism-eq/ (well-defined' f g g′ r) }) [g]

    opaque
      well-defined : ([g] : Premorphism/ R S (β .shape-mor)) (f f′ : Premorphism Q R (α .shape-mor))  PremorphismEquiv f f′  composite' [g] f  composite' [g] f′
      well-defined [g] f f′ f-equiv-f′ = SQ.elimProp {P = λ [g]  composite' [g] f  composite' [g] f′}  [g]  SQ.squash/ _ _)  g  pre-morphism-eq/ $ comp-equiv g) [g] where
        comp-equiv :  g  PremorphismEquiv (f ⋆-pre g) (f′ ⋆-pre g)
        comp-equiv g s = do
          (ρ , ρ-comm)  f-equiv-f′ s
          (σ , σ-nat)   g .symm-pres _ ρ
          return $ σ , (
              (f ⋆-pre g) .pos-mor s ≡⟨⟩
              g .pos-mor _  f .pos-mor _ ≡⟨ cong (g .pos-mor _ ⋆_) ρ-comm 
              g .pos-mor _  (ρ R.⁺)  f′ .pos-mor _ ≡⟨ cong (_⋆ f′ .pos-mor _) σ-nat 
              (σ S.⁺  g .pos-mor _)  f′ .pos-mor _ ≡⟨⟩
              (σ S.◁ (f′ ⋆-pre g) .pos-mor s) 
            )

private
  _⋆Q_ = compQContMorphism

opaque
  ⋆IdL : (α : Morphism Q S)  idQCont ⋆Q α  α
  ⋆IdL = MorphismElimProp  α  idQCont ⋆Q α  α)  α  isSetMorphism _ α)
    λ { u f  cong pre→mor (Premorphism.⋆IdL u f) }

  ⋆IdR : (α : Morphism Q S)  α ⋆Q idQCont  α
  ⋆IdR = MorphismElimProp  α  α ⋆Q idQCont  α)  α  isSetMorphism _ α)
    λ { u f  cong pre→mor (Premorphism.⋆IdR u f) }

  ⋆Assoc : (α : Morphism Q R) (β : Morphism R S) (γ : Morphism S T)  (α ⋆Q β) ⋆Q γ  α ⋆Q (β ⋆Q γ)
  ⋆Assoc = MorphismElimProp3 _  { _ _ _  isSetMorphism _ _ })
    λ u v w f g h i  pre→mor (Premorphism.⋆Assoc u v w f g h i)