open import Categories.Category.Core
open import Categories.Category.Distributive using (Distributive)
open import Categories.Category.Cartesian using (Cartesian)
open import Categories.Category.Cocartesian using (Cocartesian)
open import Categories.Category.BinaryProducts using (BinaryProducts)
open import Categories.Category.Cartesian.Bundle using (CartesianCategory)
open import Categories.Category.Cartesian.Monoidal
open import Categories.Category.Cartesian.SymmetricMonoidal using () renaming (symmetric to symm)
open import Categories.Category.Monoidal using (Monoidal)
open import Categories.Category.Monoidal.Symmetric using (Symmetric)
open import Categories.Category.Monoidal.Braided using (Braided)

import Categories.Morphism as M
import Categories.Morphism.Reasoning as MR
import Categories.Morphism.Properties as MP


-- Some helpers to make working with distributive categories easier

module Category.Distributive.Helper {o  e} {C : Category o  e} (distributive : Distributive C) where
  open Category C
  -- open cartesian and cocartesian publically, because usually both are needed when working with a distributive category.
  open Distributive distributive public
  open import Categories.Category.Distributive.Properties distributive public
  open Cocartesian cocartesian renaming (+-unique to []-unique) public
  open Cartesian cartesian public
  open BinaryProducts products renaming (η to ⁂-η; g-η to ⁂-g-η; unique to ⟨⟩-unique; unique′ to ⟨⟩-unique′) public
  open CartesianMonoidal cartesian using (⊤×A≅A; A×⊤≅A) public

  -- the codiagonal
   :  {X}  X + X  X
   = [ id , id ]



  -- other bundles that can be derived
  module Bundles where
    cartesianCategory : CartesianCategory o  e
    cartesianCategory = record { U = C ; cartesian = cartesian }

    monoidal : Monoidal C
    monoidal = CartesianMonoidal.monoidal cartesian

    symmetric : Symmetric monoidal
    symmetric = symm C cartesian

    braided : Braided monoidal
    braided = Symmetric.braided symmetric

  
  -- some lemmas
  open M C
  open MR C
  open MP C
  open HomReasoning
  open Equiv

  distributeˡ⁻¹-natural-id :  {X Y U V} {f : X  Y}  ((f  id {U}) +₁ (f  id {V}))  distributeˡ⁻¹  distributeˡ⁻¹  (f  id)
  distributeˡ⁻¹-natural-id {X} {Y} {U} {V} {f} = distributeˡ⁻¹-natural f id id  ∘-resp-≈ʳ (⁂-cong₂ refl ([]-unique id-comm-sym id-comm-sym))

  distribute₄ :  {A B C D}  (distributeʳ⁻¹ +₁ distributeʳ⁻¹)  distributeˡ⁻¹ {A + B} {C} {D}  [ i₁ +₁ i₁ , i₂ +₁ i₂ ]  (distributeˡ⁻¹ +₁ distributeˡ⁻¹)  distributeʳ⁻¹
  distribute₄ = Iso⇒Epi (IsIso.iso isIsoʳ) ((distributeʳ⁻¹ +₁ distributeʳ⁻¹)  distributeˡ⁻¹) ([ i₁ +₁ i₁ , i₂ +₁ i₂ ]  (distributeˡ⁻¹ +₁ distributeˡ⁻¹)  distributeʳ⁻¹) (begin 
    (((distributeʳ⁻¹ +₁ distributeʳ⁻¹)  distributeˡ⁻¹)  distributeʳ)                                                                                                ≈⟨ ∘[]  
    [ (((distributeʳ⁻¹ +₁ distributeʳ⁻¹)  distributeˡ⁻¹)  (i₁  id)) , (((distributeʳ⁻¹ +₁ distributeʳ⁻¹)  distributeˡ⁻¹)  (i₂  id)) ]                         ≈⟨ []-cong₂ (pullʳ ((refl⟩∘⟨ (⁂-cong₂ refl (sym ([]-unique id-comm-sym id-comm-sym))))  sym (distributeˡ⁻¹-natural i₁ id id))) (pullʳ (((refl⟩∘⟨ (⁂-cong₂ refl (sym ([]-unique id-comm-sym id-comm-sym))))  sym (distributeˡ⁻¹-natural i₂ id id))))  
    [ (distributeʳ⁻¹ +₁ distributeʳ⁻¹)  ((i₁  id) +₁ (i₁  id))  distributeˡ⁻¹ , (distributeʳ⁻¹ +₁ distributeʳ⁻¹)  ((i₂  id) +₁ (i₂  id))  distributeˡ⁻¹ ] ≈⟨ []-cong₂ (pullˡ (+₁∘+₁  +₁-cong₂ distributeʳ⁻¹-i₁ distributeʳ⁻¹-i₁)) (pullˡ (+₁∘+₁  +₁-cong₂ distributeʳ⁻¹-i₂ distributeʳ⁻¹-i₂)) 
    [ (i₁ +₁ i₁)  distributeˡ⁻¹ , (i₂ +₁ i₂)  distributeˡ⁻¹ ]                                                                                                       ≈˘⟨ []∘+₁  
    ([ i₁ +₁ i₁ , i₂ +₁ i₂ ]  (distributeˡ⁻¹ +₁ distributeˡ⁻¹))                                                                                                      ≈˘⟨ pullʳ (cancelʳ (IsIso.isoˡ isIsoʳ))  
    (([ i₁ +₁ i₁ , i₂ +₁ i₂ ]  (distributeˡ⁻¹ +₁ distributeˡ⁻¹)  distributeʳ⁻¹)  distributeʳ)                                                                      )

  distributeˡ⁻¹-assoc :  {A B C D : Obj}  distributeˡ⁻¹  (id  distributeˡ⁻¹)  _≅_.to ×-assoc  (_≅_.to ×-assoc +₁ _≅_.to ×-assoc)  distributeˡ⁻¹ {A × B} {C} {D}
  distributeˡ⁻¹-assoc {A} {B} {U} {D} = Iso⇒Epi (IsIso.iso isIsoˡ) (distributeˡ⁻¹  (id  distributeˡ⁻¹)  _≅_.to ×-assoc) ((_≅_.to ×-assoc +₁ _≅_.to ×-assoc)  distributeˡ⁻¹) (begin 
    (distributeˡ⁻¹  (id  distributeˡ⁻¹)  _≅_.to ×-assoc)  [ id  i₁ , id  i₂ ]                                                                                                                          ≈⟨ ∘[]  
    [ (distributeˡ⁻¹  (id  distributeˡ⁻¹)  _≅_.to ×-assoc)  (id  i₁) , (distributeˡ⁻¹  (id  distributeˡ⁻¹)  _≅_.to ×-assoc)  (id  i₂) ]                                                           ≈⟨ []-cong₂ (pullʳ (pullʳ ⟨⟩∘)) (pullʳ (pullʳ ⟨⟩∘))  
    [ distributeˡ⁻¹  (id  distributeˡ⁻¹)   (π₁  π₁)  (id  i₁) ,  π₂  π₁ , π₂   (id  i₁)  , distributeˡ⁻¹  (id  distributeˡ⁻¹)   (π₁  π₁)  (id  i₂) ,  π₂  π₁ , π₂   (id  i₂)  ] ≈⟨ []-cong₂ (refl⟩∘⟨ ⁂∘⟨⟩) (refl⟩∘⟨ ⁂∘⟨⟩)  
    [ distributeˡ⁻¹   id  (π₁  π₁)  (id  i₁) , distributeˡ⁻¹   π₂  π₁ , π₂   (id  i₁)  , distributeˡ⁻¹   id  (π₁  π₁)  (id  i₂) , distributeˡ⁻¹   π₂  π₁ , π₂   (id  i₂)  ]     ≈⟨ []-cong₂ (refl⟩∘⟨ (⟨⟩-cong₂ identityˡ refl)) (refl⟩∘⟨ (⟨⟩-cong₂ identityˡ refl))  
    [ distributeˡ⁻¹   (π₁  π₁)  (id  i₁) , distributeˡ⁻¹   π₂  π₁ , π₂   (id  i₁)  , distributeˡ⁻¹   (π₁  π₁)  (id  i₂) , distributeˡ⁻¹   π₂  π₁ , π₂   (id  i₂)  ]                 ≈⟨ []-cong₂ (refl⟩∘⟨ (⟨⟩-cong₂ (pullʳ π₁∘⁂) (refl⟩∘⟨ ⟨⟩∘))) (refl⟩∘⟨ ⟨⟩-cong₂ (pullʳ π₁∘⁂) (refl⟩∘⟨ ⟨⟩∘))  
    [ distributeˡ⁻¹   π₁  id  π₁ , distributeˡ⁻¹   (π₂  π₁)  (id  i₁) , π₂  (id  i₁)   , distributeˡ⁻¹   π₁  id  π₁ , distributeˡ⁻¹   (π₂  π₁)  (id  i₂) , π₂  (id  i₂)   ]     ≈⟨ []-cong₂ (refl⟩∘⟨ (⟨⟩-cong₂ (refl⟩∘⟨ identityˡ) (refl⟩∘⟨ (⟨⟩-cong₂ (pullʳ π₁∘⁂) π₂∘⁂)))) (refl⟩∘⟨ (⟨⟩-cong₂ (refl⟩∘⟨ identityˡ) (refl⟩∘⟨ (⟨⟩-cong₂ (pullʳ π₁∘⁂) π₂∘⁂))))  
    [ distributeˡ⁻¹   π₁  π₁ , distributeˡ⁻¹   π₂  id  π₁ , i₁  π₂   , distributeˡ⁻¹   π₁  π₁ , distributeˡ⁻¹   π₂  id  π₁ , i₂  π₂   ]                                                   ≈⟨ []-cong₂ (refl⟩∘⟨ ⟨⟩-cong₂ refl (refl⟩∘⟨ ⟨⟩-cong₂ ((refl⟩∘⟨ identityˡ)  sym identityˡ) refl)) (refl⟩∘⟨ ⟨⟩-cong₂ refl (refl⟩∘⟨ ⟨⟩-cong₂ ((refl⟩∘⟨ identityˡ)  sym identityˡ) refl))  
    [ distributeˡ⁻¹   π₁  π₁ , distributeˡ⁻¹   id  π₂  π₁ , i₁  π₂   , distributeˡ⁻¹   π₁  π₁ , distributeˡ⁻¹   id  π₂  π₁ , i₂  π₂   ]                                                   ≈˘⟨ []-cong₂ (refl⟩∘⟨ (⟨⟩-cong₂ refl (refl⟩∘⟨ ⁂∘⟨⟩))) (refl⟩∘⟨ (⟨⟩-cong₂ refl (refl⟩∘⟨ ⁂∘⟨⟩)))   
    [ distributeˡ⁻¹   π₁  π₁ , distributeˡ⁻¹  (id  i₁)   π₂  π₁ , π₂   , distributeˡ⁻¹   π₁  π₁ , distributeˡ⁻¹  (id  i₂)   π₂  π₁ , π₂   ]                                               ≈⟨ []-cong₂ (refl⟩∘⟨ (⟨⟩-cong₂ (sym identityˡ) (pullˡ distributeˡ⁻¹-i₁))) (refl⟩∘⟨ (⟨⟩-cong₂ (sym identityˡ) (pullˡ distributeˡ⁻¹-i₂)))  
    [ distributeˡ⁻¹   id  π₁  π₁ , i₁   π₂  π₁ , π₂   , distributeˡ⁻¹   id  π₁  π₁ , i₂   π₂  π₁ , π₂   ]                                                                                   ≈˘⟨ []-cong₂ (refl⟩∘⟨ ⁂∘⟨⟩) (refl⟩∘⟨ ⁂∘⟨⟩)  
    [ distributeˡ⁻¹  (id  i₁)   π₁  π₁ ,  π₂  π₁ , π₂   , distributeˡ⁻¹  (id  i₂)   π₁  π₁ ,  π₂  π₁ , π₂   ]                                                                               ≈⟨ []-cong₂ (pullˡ distributeˡ⁻¹-i₁) (pullˡ distributeˡ⁻¹-i₂)  
    (_≅_.to ×-assoc +₁ _≅_.to ×-assoc)                                                                                                                                                                          ≈˘⟨ cancelʳ (IsIso.isoˡ isIsoˡ)  
    ((_≅_.to ×-assoc +₁ _≅_.to ×-assoc)  distributeˡ⁻¹)  distributeˡ                                                                                                                                          )

  [⟨⟩]≈⟨[]⟩ :  {A B C D} (f : A  B) (g : A  C) (h : D  B) (i : D  C)  [  f , g  ,  h , i  ]   [ f , h ] , [ g , i ] 
  [⟨⟩]≈⟨[]⟩ f g h i = []-unique (⟨⟩∘  ⟨⟩-cong₂ inject₁ inject₁) (⟨⟩∘  ⟨⟩-cong₂ inject₂ inject₂)