{-# OPTIONS --cubical --no-import-sorts --safe #-}
module Cubical.Data.Sum.Properties where

open import Cubical.Core.Everything
open import Cubical.Foundations.Prelude
open import Cubical.Foundations.HLevels
open import Cubical.Functions.Embedding
open import Cubical.Foundations.Equiv
open import Cubical.Foundations.Isomorphism
open import Cubical.Data.Empty
open import Cubical.Data.Nat

open import Cubical.Data.Sum.Base

-- Path space of sum type
module SumPath { ℓ'} {A : Type } {B : Type ℓ'} where

  Cover : A  B  A  B  Type (ℓ-max  ℓ')
  Cover (inl a) (inl a') = Lift {j = ℓ-max  ℓ'} (a  a')
  Cover (inl _) (inr _) = Lift 
  Cover (inr _) (inl _) = Lift 
  Cover (inr b) (inr b') = Lift {j = ℓ-max  ℓ'} (b  b')

  reflCode : (c : A  B)  Cover c c
  reflCode (inl a) = lift refl
  reflCode (inr b) = lift refl

  encode :  c c'  c  c'  Cover c c'
  encode c _ = J  c' _  Cover c c') (reflCode c)

  encodeRefl :  c  encode c c refl  reflCode c
  encodeRefl c = JRefl  c' _  Cover c c') (reflCode c)

  decode :  c c'  Cover c c'  c  c'
  decode (inl a) (inl a') (lift p) = cong inl p
  decode (inl a) (inr b') ()
  decode (inr b) (inl a') ()
  decode (inr b) (inr b') (lift q) = cong inr q

  decodeRefl :  c  decode c c (reflCode c)  refl
  decodeRefl (inl a) = refl
  decodeRefl (inr b) = refl

  decodeEncode :  c c'  (p : c  c')  decode c c' (encode c c' p)  p
  decodeEncode c _ =
    J  c' p  decode c c' (encode c c' p)  p)
      (cong (decode c c) (encodeRefl c)  decodeRefl c)

  encodeDecode :  c c'  (d : Cover c c')  encode c c' (decode c c' d)  d
  encodeDecode (inl a) (inl _) (lift d) =
    J  a' p  encode (inl a) (inl a') (cong inl p)  lift p) (encodeRefl (inl a)) d
  encodeDecode (inr a) (inr _) (lift d) =
    J  a' p  encode (inr a) (inr a') (cong inr p)  lift p) (encodeRefl (inr a)) d

  Cover≃Path :  c c'  Cover c c'  (c  c')
  Cover≃Path c c' =
    isoToEquiv (iso (decode c c') (encode c c') (decodeEncode c c') (encodeDecode c c'))

  isOfHLevelCover : (n : HLevel)
     isOfHLevel (suc (suc n)) A
     isOfHLevel (suc (suc n)) B
      c c'  isOfHLevel (suc n) (Cover c c')
  isOfHLevelCover n p q (inl a) (inl a') = isOfHLevelLift (suc n) (p a a')
  isOfHLevelCover n p q (inl a) (inr b') =
    isOfHLevelLift (suc n) (isProp→isOfHLevelSuc n isProp⊥)
  isOfHLevelCover n p q (inr b) (inl a') =
    isOfHLevelLift (suc n) (isProp→isOfHLevelSuc n isProp⊥)
  isOfHLevelCover n p q (inr b) (inr b') = isOfHLevelLift (suc n) (q b b')

isEmbedding-inl :  { ℓ'} {A : Type } {B : Type ℓ'}  isEmbedding (inl {A = A} {B = B})
isEmbedding-inl w z = snd (compEquiv LiftEquiv (SumPath.Cover≃Path (inl w) (inl z)))

isEmbedding-inr :  { ℓ'} {A : Type } {B : Type ℓ'}  isEmbedding (inr {A = A} {B = B})
isEmbedding-inr w z = snd (compEquiv LiftEquiv (SumPath.Cover≃Path (inr w) (inr z)))

isOfHLevelSum :  { ℓ'} (n : HLevel) {A : Type } {B : Type ℓ'}
   isOfHLevel (suc (suc n)) A
   isOfHLevel (suc (suc n)) B
   isOfHLevel (suc (suc n)) (A  B)
isOfHLevelSum n lA lB c c' =
  isOfHLevelRetract (suc n)
    (SumPath.encode c c')
    (SumPath.decode c c')
    (SumPath.decodeEncode c c')
    (SumPath.isOfHLevelCover n lA lB c c')

isSetSum :  { ℓ'} {A : Type } {B : Type ℓ'}  isSet A  isSet B  isSet (A  B)
isSetSum = isOfHLevelSum 0

isGroupoidSum :  { ℓ'} {A : Type } {B : Type ℓ'}  isGroupoid A  isGroupoid B  isGroupoid (A  B)
isGroupoidSum = isOfHLevelSum 1

is2GroupoidSum :  { ℓ'} {A : Type } {B : Type ℓ'}  is2Groupoid A  is2Groupoid B  is2Groupoid (A  B)
is2GroupoidSum = isOfHLevelSum 2

sumIso :  {ℓa ℓb ℓc ℓd} {A : Type ℓa} {B : Type ℓb} {C : Type ℓc} {D : Type ℓd}
        Iso A C  Iso B D
        Iso (A  B) (C  D)
Iso.fun (sumIso iac ibd) (inl x) = inl (iac .Iso.fun x)
Iso.fun (sumIso iac ibd) (inr x) = inr (ibd .Iso.fun x)
Iso.inv (sumIso iac ibd) (inl x) = inl (iac .Iso.inv x)
Iso.inv (sumIso iac ibd) (inr x) = inr (ibd .Iso.inv x)
Iso.rightInv (sumIso iac ibd) (inl x) = cong inl (iac .Iso.rightInv x)
Iso.rightInv (sumIso iac ibd) (inr x) = cong inr (ibd .Iso.rightInv x)
Iso.leftInv (sumIso iac ibd) (inl x) = cong inl (iac .Iso.leftInv x)
Iso.leftInv (sumIso iac ibd) (inr x) = cong inr (ibd .Iso.leftInv x)