module Cubical.Data.IterativeSets.Base where

open import Cubical.Foundations.Prelude

open import Cubical.Foundations.Function
open import Cubical.Foundations.Equiv
open import Cubical.Foundations.Isomorphism
open import Cubical.Functions.Embedding
open import Cubical.Foundations.HLevels
open import Cubical.Data.Sigma
open import Cubical.Relation.Nullary using (¬_)
open import Cubical.Foundations.GroupoidLaws
open import Cubical.Functions.Fibration
open import Cubical.Foundations.Transport
open import Cubical.Foundations.Path
open import Cubical.Foundations.Equiv.Properties
open import Cubical.Foundations.Equiv.Fiberwise

open import Cubical.Data.IterativeMultisets.Base renaming (index to index-∞ ; elements to elements-V∞ ; toFib to toFib-∞)

private
  variable
     : Level

isIterativeSet : V∞ {}  Type (ℓ-suc )
isIterativeSet (sup-∞ A f) = (isEmbedding f) × ((a : A)  isIterativeSet (f a))

isPropIsIterativeSet : (x : V∞ {})  isProp (isIterativeSet x)
isPropIsIterativeSet (sup-∞ A f) = isProp× isPropIsEmbedding helper
  where
    helper : isProp ((a : A)  isIterativeSet (f a))
    helper g h i x = isPropIsIterativeSet (f x) (g x) (h x) i

V⁰ : Type (ℓ-suc )
V⁰ = Σ[ x  V∞ ] isIterativeSet x

private
  variable
    x y z : V⁰ {}

-- accessing the components

index : V⁰ {}  Type 
index = index-∞  fst

elements-∞ : (x : V⁰ {})  index x  V∞ {}
elements-∞ = elements-V∞  fst

elements : (x : V⁰ {})  index x  V⁰ {}
elements (sup-∞ _ f , _) a .fst = f a
elements (sup-∞ _ _ , isitset) a .snd = isitset .snd a

isEmbedding-elements-∞ : (x : V⁰ {})  isEmbedding (elements-∞ x)
isEmbedding-elements-∞ (sup-∞ _ _ , its) = its .fst

isEmbedding-elements : (x : V⁰ {})  isEmbedding (elements x)
isEmbedding-elements (sup-∞ _ _ , isitset) = isEmbeddingSndΣProp isPropIsIterativeSet _ (isitset .fst)

Embedding-elements : (x : V⁰ {})  index x  V⁰ {}
Embedding-elements x .fst = elements x
Embedding-elements x .snd = isEmbedding-elements x

V⁰↪V∞ : V⁰ {}  V∞ {}
V⁰↪V∞ = EmbeddingΣProp isPropIsIterativeSet

≡V⁰-≃-≡V∞ : (x  y)  (x .fst  y .fst)
≡V⁰-≃-≡V∞ .fst = cong fst
≡V⁰-≃-≡V∞ .snd = V⁰↪V∞ .snd _ _

_∈⁰_ : V⁰ {}  V⁰ {}  Type (ℓ-suc )
x ∈⁰ y = fiber (elements y) (x)

∈⁰-irrefl : ¬ x ∈⁰ x
∈⁰-irrefl {x = sup-∞ A f , _} (a , p) = ∈∞-irrefl {x = sup-∞ A f} (a , cong fst p)

Iso-V⁰-Emb : Iso (V⁰ {}) (Embedding (V⁰ {}) )
Iso-V⁰-Emb {} = compIso isom Σ-assoc-Iso
  where
    isom : Iso (V⁰ {}) (Σ[ F  Fibration (V⁰ {})  ] isEmbedding (F .snd))
    isom .Iso.fun (sup-∞ A f , its) .fst .fst = index (sup-∞ A f , its)
    isom .Iso.fun (sup-∞ A f , its) .fst .snd a .fst = f a
    isom .Iso.fun (sup-∞ A f , its) .fst .snd a .snd = its .snd a
    isom .Iso.fun (sup-∞ A f , its) .snd = isEmbeddingSndΣProp isPropIsIterativeSet _ (its .fst)
    isom .Iso.inv E .fst = sup-∞ (E .fst .fst) (compEmbedding V⁰↪V∞ (E .fst .snd , E .snd) .fst)
    isom .Iso.inv E .snd .fst = compEmbedding V⁰↪V∞ (E .fst .snd , E .snd) .snd
    isom .Iso.inv E .snd .snd a = E .fst .snd a .snd
    isom .Iso.sec E = Σ≡Prop  _  isPropIsEmbedding) refl
    isom .Iso.ret (sup-∞ _ _ , _) = Σ≡Prop isPropIsIterativeSet refl

toEmb : V⁰ {}  Embedding (V⁰ {}) 
toEmb = Iso-V⁰-Emb .Iso.fun

fromEmb : Embedding (V⁰ {})   V⁰ {}
fromEmb = Iso-V⁰-Emb .Iso.inv

-- TODO: figure out why this one computes poorly
secEmb : section (toEmb {}) (fromEmb {})
secEmb = Iso-V⁰-Emb .Iso.sec

retEmb : retract (toEmb {}) (fromEmb {})
retEmb = Iso-V⁰-Emb .Iso.ret

V⁰≃Emb : V⁰ {}  Embedding (V⁰ {}) 
V⁰≃Emb = isoToEquiv Iso-V⁰-Emb

Emb≃V⁰ : Embedding (V⁰ {})   V⁰ {}
Emb≃V⁰ = isoToEquiv (invIso Iso-V⁰-Emb)

isSetV⁰ : isSet (V⁰ {})
isSetV⁰ = isOfHLevelRespectEquiv 2 Emb≃V⁰ isSetEmbedding

_≃V⁰_ : (x y : V⁰ {})  Type (ℓ-suc )
x ≃V⁰ y = ((z : V⁰)  (z ∈⁰ x)  (z ∈⁰ y)) ×
          ((z : V⁰)  (z ∈⁰ y)  (z ∈⁰ x))

≃V⁰-≃-≡V⁰ : { : Level} {x y : V⁰ {}}  (x ≃V⁰ y)  (x  y)
≃V⁰-≃-≡V⁰ {x = sup-∞ A f , itsx} {y = sup-∞ B g , itsy} =
    let
        x = sup-∞ A f , itsx
        y = sup-∞ B g , itsy
    in compEquiv (EmbeddingIP (toEmb x) (toEmb y)) (invEquiv (cong toEmb , iso→isEmbedding Iso-V⁰-Emb x y))

≡V⁰-≃-≃V⁰ : { : Level} {x y : V⁰ {}}  (x  y)  (x ≃V⁰ y)
≡V⁰-≃-≃V⁰ {x = sup-∞ A f , itsx} {y = sup-∞ B g , itsy} =
    let
        x = sup-∞ A f , itsx
        y = sup-∞ B g , itsy
    in compEquiv (cong toEmb , iso→isEmbedding Iso-V⁰-Emb x y) (invEquiv (EmbeddingIP (toEmb x) (toEmb y)))

V⁰↪Fib : (V⁰ {})  Fibration (V⁰ {}) 
V⁰↪Fib {} = compEmbedding Emb↪Fib (Iso→Embedding Iso-V⁰-Emb)
  where
    open EmbeddingIdentityPrinciple
    Emb↪Fib : Embedding (V⁰ {})   Fibration (V⁰ {}) 
    Emb↪Fib .fst = toFibr
    Emb↪Fib .snd = isEmbeddingToFibr

toFib : (V⁰ {})  Fibration (V⁰ {}) 
toFib = V⁰↪Fib .fst

_≃V⁰'_ : (x y : V⁰ {})  Type (ℓ-suc )
x ≃V⁰' y = (z : V⁰)  ((z ∈⁰ x)  (z ∈⁰ y))

≃V⁰'-≃-≡V⁰ : { : Level} {x y : V⁰ {}}  (x ≃V⁰' y)  (x  y)
≃V⁰'-≃-≡V⁰ {x = sup-∞ A f , itsx} {y = sup-∞ B g , itsy} =
    let
        x = sup-∞ A f , itsx
        y = sup-∞ B g , itsy
    in compEquiv (FibrationIP (toFib x) (toFib y)) (invEquiv (cong toFib , V⁰↪Fib .snd x y))

≡V⁰-≃-≃V⁰' : { : Level} {x y : V⁰ {}}  (x  y)  (x ≃V⁰' y)
≡V⁰-≃-≃V⁰' {x = sup-∞ A f , itsx} {y = sup-∞ B g , itsy} =
    let
        x = sup-∞ A f , itsx
        y = sup-∞ B g , itsy
    in compEquiv (cong toFib , V⁰↪Fib .snd x y) (invEquiv (FibrationIP (toFib x) (toFib y)))

isProp∈∞ : {z : V∞ {}}  isProp (z ∈∞ (x .fst))
isProp∈∞ {x = x} {z = z} = isEmbedding→hasPropFibers (isEmbedding-elements-∞ x) z

isProp∈⁰ : {x z : V⁰ {}}  isProp (z ∈⁰ x)
isProp∈⁰ {x = x} {z = z} = isEmbedding→hasPropFibers (isEmbedding-elements x) z

El⁰ : V⁰ {}  Type 
El⁰ = index

fromEmb' : (x : V⁰ {})  (El⁰ x  V⁰ {})
fromEmb' (sup-∞ A f , its) = toEmb (sup-∞ A f , its) .snd

isSetEl⁰ : (x : V⁰ {})  isSet (El⁰ x)
isSetEl⁰ {} x = Embedding-into-isSet→isSet {A = El⁰ {} x} {B = V⁰ {}} (fromEmb' x) (isSetV⁰ {})

isProp-∈⁰-Equiv : (x y : V⁰ {})  isProp ((z : V⁰)  (z ∈⁰ x)  (z ∈⁰ y))
isProp-∈⁰-Equiv x y = isPropΠ λ z  isOfHLevel≃ 1 (isProp∈⁰ {x = x} {z = z}) (isProp∈⁰ {x = y} {z = z})

∈⁰≃∈∞ : {x z : V⁰ {}}  (z ∈⁰ x)  (z .fst ∈∞ x .fst)
∈⁰≃∈∞ {x = sup-∞ x α , itsetx} {z = sup-∞ z γ , itsetz} = propBiimpl→Equiv
      (isProp∈⁰ {x = sup-∞ x α , itsetx} {z = sup-∞ z γ , itsetz})
      (isProp∈∞ {x = sup-∞ x α , itsetx} {z = sup-∞ z γ})
      f g
    where
        f : (sup-∞ z γ , itsetz) ∈⁰ (sup-∞ x α , itsetx)  sup-∞ z γ ∈∞ sup-∞ x α
        f (a , p) .fst = a
        f (a , p) .snd = cong fst p
        g : sup-∞ z γ ∈∞ sup-∞ x α  (sup-∞ z γ , itsetz) ∈⁰ (sup-∞ x α , itsetx)
        g (a , p) .fst = a
        g (a , p) .snd = Σ≡Prop isPropIsIterativeSet p