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)