{-# OPTIONS --without-K --rewriting #-}

open import lib.Basics
open import lib.cubical.Square
open import lib.types.Group
open import lib.types.EilenbergMacLane1.Core
open import lib.types.EilenbergMacLane1.DoubleElim

module lib.types.EilenbergMacLane1.DoublePathElim where

private

  module _ {i j} {A : Type i} {B : Type j} {f g : A  B} where

    custom-square-over :
      {x y z : A} {p : x == y} {q : y == z} {r : x == z}
      (comp : r == p  q)
      {u : f x == g x} {v : f y == g y} {w : f z == g z}
      (p-sq : Square u (ap f p) (ap g p) v)
      (q-sq : Square v (ap f q) (ap g q) w)
      (r-sq : Square u (ap f r) (ap g r) w)
       r-sq ⊡v∙ ap (ap g) comp ==
        ap (ap f) comp ∙v⊡ ↓-='-square-comp p-sq q-sq
       r-sq == ↓-='-square-comp p-sq q-sq
          [  s  Square u (ap f s) (ap g s) w)  comp ]
    custom-square-over {p = idp} {q = idp} {r = .idp} idp p-sq q-sq r-sq e = e

    embase-emloop-comp-helper :
      {x y z : A} {p : x == y} {q : y == z} {r : x == z}
      (comp : r == p  q)
      {u : f x == g x} {v : f y == g y} {w : f z == g z}
      (p-sq : Square u (ap f p) (ap g p) v)
      (q-sq : Square v (ap f q) (ap g q) w)
      (r-sq : Square u (ap f r) (ap g r) w)
       r-sq ⊡v∙ ap (ap g) comp ==
        ap (ap f) comp ∙v⊡ ↓-='-square-comp p-sq q-sq
       ↓-='-from-square r-sq == ↓-='-from-square p-sq ∙ᵈ ↓-='-from-square q-sq
          [  p  u == w [  x  f x == g x)  p ])  comp ]
    embase-emloop-comp-helper comp p-sq q-sq r-sq e =
      ap↓ ↓-='-from-square (custom-square-over comp p-sq q-sq r-sq e) 
      ↓-='-from-square-comp p-sq q-sq

  emloop-emloop-eq-helper :  {i j k} {A : Type i} {B : Type j} {C : Type k}
    (f g : A  B  C)
    {a₀ a₁ : A} (p : a₀ == a₁)
    {b₀ b₁ : B} (q : b₀ == b₁)
    {u₀₀ : f a₀ b₀ == g a₀ b₀}
    {u₀₁ : f a₀ b₁ == g a₀ b₁}
    {u₁₀ : f a₁ b₀ == g a₁ b₀}
    {u₁₁ : f a₁ b₁ == g a₁ b₁}
    (sq₀₋ : Square u₀₀ (ap (f a₀) q) (ap (g a₀) q) u₀₁)
    (sq₁₋ : Square u₁₀ (ap (f a₁) q) (ap (g a₁) q) u₁₁)
    (sq₋₀ : Square u₀₀ (ap  a  f a b₀) p) (ap  a  g a b₀) p) u₁₀)
    (sq₋₁ : Square u₀₁ (ap  a  f a b₁) p) (ap  a  g a b₁) p) u₁₁)
     ap-comm f p q ∙v⊡ (sq₀₋ ⊡h sq₋₁)
      ==
      (sq₋₀ ⊡h sq₁₋) ⊡v∙ ap-comm g p q
     ↓-ap-in  Z  Z)  a  f a b₀ == g a b₀) (↓-='-from-square sq₋₀) ∙'ᵈ
      ↓-ap-in  Z  Z)  b  f a₁ b == g a₁ b) (↓-='-from-square sq₁₋)
      ==
      ↓-ap-in  Z  Z)  b  f a₀ b == g a₀ b) (↓-='-from-square sq₀₋) ∙ᵈ
      ↓-ap-in  Z  Z)  a  f a b₁ == g a b₁) (↓-='-from-square sq₋₁)
        [  p  u₀₀ == u₁₁ [  Z  Z)  p ]) 
          ap-comm'  a b  f a b == g a b) p q ]
  emloop-emloop-eq-helper f g idp idp sq₀₋ sq₁₋ sq₋₀ sq₋₁ r =
    horiz-degen-path sq₋₀ ∙' horiz-degen-path sq₁₋
      =⟨ ∙'=∙ (horiz-degen-path sq₋₀) (horiz-degen-path sq₁₋) 
    horiz-degen-path sq₋₀  horiz-degen-path sq₁₋
      =⟨ ! (horiz-degen-path-⊡h sq₋₀ sq₁₋) 
    horiz-degen-path (sq₋₀ ⊡h sq₁₋)
      =⟨ ap horiz-degen-path (! r) 
    horiz-degen-path (sq₀₋ ⊡h sq₋₁)
      =⟨ horiz-degen-path-⊡h sq₀₋ sq₋₁ 
    horiz-degen-path sq₀₋  horiz-degen-path sq₋₁ =∎

module _ {i j} (G : Group i) (H : Group j) where

  private
    module G = Group G
    module H = Group H

  module EM₁Level₂DoublePathElim {k} {C : Type k}
    {{C-level : has-level 2 C}}
    (f₁ f₂ : EM₁ G  EM₁ H  C)
    (embase-embase* : f₁ embase embase == f₂ embase embase)
    (embase-emloop* :  h 
      Square embase-embase* (ap (f₁ embase) (emloop h))
             (ap (f₂ embase) (emloop h)) embase-embase*)
    (emloop-embase* :  g 
      Square embase-embase* (ap  x  f₁ x embase) (emloop g))
             (ap  x  f₂ x embase) (emloop g)) embase-embase*)
    (embase-emloop-comp* :  h₁ h₂ 
      embase-emloop* (H.comp h₁ h₂) ⊡v∙
      ap (ap (f₂ embase)) (emloop-comp' H h₁ h₂)
      ==
      ap (ap (f₁ embase)) (emloop-comp' H h₁ h₂) ∙v⊡
      ↓-='-square-comp (embase-emloop* h₁) (embase-emloop* h₂))
    (emloop-comp-embase* :  g₁ g₂ 
      emloop-embase* (G.comp g₁ g₂) ⊡v∙
      ap (ap  x  f₂ x embase)) (emloop-comp' G g₁ g₂)
      ==
      ap (ap  x  f₁ x embase)) (emloop-comp' G g₁ g₂) ∙v⊡
      ↓-='-square-comp (emloop-embase* g₁) (emloop-embase* g₂))
    (emloop-emloop* :  g h 
      ap-comm f₁ (emloop g) (emloop h) ∙v⊡
      (embase-emloop* h ⊡h emloop-embase* g)
      ==
      (emloop-embase* g ⊡h embase-emloop* h) ⊡v∙
      ap-comm f₂ (emloop g) (emloop h))
    where

    private
      module M =
        EM₁Level₁DoubleElim G H
          {P = λ x y  f₁ x y == f₂ x y}
          {{has-level-apply C-level _ _}}
          embase-embase*
           h  ↓-='-from-square (embase-emloop* h))
           g  ↓-='-from-square (emloop-embase* g))
           h₁ h₂ 
            embase-emloop-comp-helper (emloop-comp h₁ h₂)
                                      (embase-emloop* h₁)
                                      (embase-emloop* h₂)
                                      (embase-emloop* (H.comp h₁ h₂))
                                      (embase-emloop-comp* h₁ h₂))
           g₁ g₂ 
            embase-emloop-comp-helper (emloop-comp g₁ g₂)
                                      (emloop-embase* g₁)
                                      (emloop-embase* g₂)
                                      (emloop-embase* (G.comp g₁ g₂))
                                      (emloop-comp-embase* g₁ g₂))
           g h 
            emloop-emloop-eq-helper f₁ f₂
                                    (emloop g) (emloop h)
                                    (embase-emloop* h)
                                    (embase-emloop* h)
                                    (emloop-embase* g)
                                    (emloop-embase* g)
                                    (emloop-emloop* g h) )

    open M public