open import GpdCont.Prelude

module GpdCont.Group.Pi ( : Level) where

open import GpdCont.HomotopySet
open import GpdCont.Equiv using (isSet→section-equivToIso)

open import Cubical.Foundations.Equiv
open import Cubical.Foundations.Isomorphism
open import Cubical.Foundations.HLevels
open import Cubical.Data.Sigma
open import Cubical.Categories.Category.Base
open import Cubical.Categories.Instances.Groups
open import Cubical.Categories.Adjoint.UniversalElements
open import Cubical.Categories.Presheaf.Representable

open import Cubical.Algebra.Group.Base
open import Cubical.Algebra.Group.Morphisms
open import Cubical.Algebra.Group.MorphismProperties
open import Cubical.Algebra.Group.Instances.Pi using (ΠGroup)

private
  module Group = Category (GroupCategory {})

module _ (K : hSet ) (G :  K   Group.ob) where
  open import GpdCont.Categories.Products (GroupCategory {}) 

  private
    Group→hSet : (H : Group.ob)  hSet 
    Group→hSet H .fst =  H 
    Group→hSet H .snd = str H .GroupStr.is-set

    Π : Group.ob
    Π = ΠGroup G

    proj :  k  Group.Hom[ Π , G k ]
    proj k .fst φ = φ k
    proj k .snd .IsGroupHom.pres· φ ψ = refl
    proj k .snd .IsGroupHom.pres1 = refl
    proj k .snd .IsGroupHom.presinv φ = refl

  univ-iso : (H : Group.ob)  Iso Group.Hom[ H , Π ] (∀ k  Group.Hom[ H , G k ])
  univ-iso H =
    Group.Hom[ H , Π ] Iso⟨⟩
    Σ[ φ  ( H   (k :  K )   G k ) ] IsGroupHom (str H) φ (str Π) Iso⟨ Σ-cong-iso-fst flipIso 
    Σ[ φ  ((k :  K )   H    G k ) ] IsGroupHom (str H) (flip φ) (str Π) Iso⟨ Σ-cong-iso-snd is-group-hom-iso 
    Σ[ φ  ((k :  K )   H    G k ) ] (∀ k  IsGroupHom (str H) (φ k) (str (G k))) Iso⟨ invIso Σ-Π-Iso 
    (∀ k  Group.Hom[ H , G k ]) Iso∎
    where
      is-group-hom-iso : (φ : (k :  K )   H    G k )
         Iso
          (IsGroupHom (str H) (flip φ) (str Π))
          (∀ k  IsGroupHom (str H) (φ k) (str (G k)))
      is-group-hom-iso φ = isProp→Iso
        (isPropIsGroupHom _ _)
        (isPropΠ λ k  isPropIsGroupHom _ _)
         where
          φ*-hom k .IsGroupHom.pres· g h  φ*-hom .IsGroupHom.pres· g h ≡$ k
          φ*-hom k .IsGroupHom.presinv g  φ*-hom .IsGroupHom.presinv g ≡$ k
          φ*-hom k .IsGroupHom.pres1  φ*-hom .IsGroupHom.pres1 ≡$ k
        )
         where
          φ-hom .IsGroupHom.pres· g h  funExt  k  φ-hom k .IsGroupHom.pres· g h)
          φ-hom .IsGroupHom.presinv g  funExt  k  φ-hom k .IsGroupHom.presinv g)
          φ-hom .IsGroupHom.pres1  funExt  k  φ-hom k .IsGroupHom.pres1)
        )

  private module _ (H : Group.ob) where
    univ-fun : Group.Hom[ H , Π ]  (∀ k  Group.Hom[ H , G k ])
    univ-fun φ = λ k  φ Group.⋆ (proj k)

    univ-equiv' : Group.Hom[ H , Π ]  (∀ k  Group.Hom[ H , G k ])
    univ-equiv' = isoToEquiv (univ-iso H)

    univ-fun≡ : equivFun univ-equiv'  univ-fun
    univ-fun≡ = funExt λ φ  funExt λ k  GroupHom≡ refl

    is-univ : isEquiv univ-fun
    is-univ = subst {x = equivFun univ-equiv'} {y = univ-fun} isEquiv univ-fun≡ (equivIsEquiv univ-equiv')

    univ-equiv : Group.Hom[ H , Π ]  (∀ k  Group.Hom[ H , G k ])
    univ-equiv .fst = _
    univ-equiv .snd = is-univ

    univ-equiv≡ : univ-equiv'  univ-equiv
    univ-equiv≡ = equivEq univ-fun≡

  GroupProduct : Product K G
  GroupProduct .UniversalElement.vertex = ΠGroup G
  GroupProduct .UniversalElement.element = proj
  GroupProduct .UniversalElement.universal = is-univ

  module _ (H : Group.ob) where
    opaque
      univ-coherence : NotationAt.univ-iso K G GroupProduct H  univ-iso H
      univ-coherence =
        equivToIso (univ-equiv H) ≡⟨ cong equivToIso (sym (univ-equiv≡ H)) 
        equivToIso (isoToEquiv (univ-iso H)) ≡⟨ isSet→section-equivToIso Group.isSetHom (isSetΠ  k  Group.isSetHom)) (univ-iso H) 
        univ-iso H 

    univ-inv-coherence : Iso.inv (NotationAt.univ-iso K G GroupProduct H)  univ-iso H .Iso.inv
    univ-inv-coherence = cong Iso.inv univ-coherence

GroupProducts = GroupProduct