open import Cubical.Foundations.Prelude hiding (empty)
open import Cubical.Foundations.Equiv
open import Cubical.Foundations.Function
open import Cubical.Foundations.Univalence hiding (Glue; glue)

open import Cubical.Data.List
open import Cubical.Data.Sigma hiding (empty)

module Queue.Glued
  (ABS : Type) (ABS-isProp : isProp ABS)
  (E : Type) (e₀ : E) (ESet : isSet E) where

open import Modality.Glue ABS ABS-isProp
open import Queue.Base ABS ABS-isProp E e₀ ESet

private
  revAppend : List E × List E  List E
  revAppend (l₁ , l₂) = l₂ ++ rev l₁


batchedPreQueue : PreQueue
batchedPreQueue .X = Glue' revAppend

batchedPreQueue .empty = triangle empty-⊤ empty-abs empty-correct
  where
    empty-⊤ : List E × List E
    empty-⊤ = [] , []

    empty-abs : List E
    empty-abs = listPreQueue .empty

    empty-correct : revAppend empty-⊤  empty-abs
    empty-correct = refl

batchedPreQueue .enqueue e =
  square (enqueue-⊤ e) (enqueue-abs e) enqueue-correct
  where
    enqueue-⊤ : E  List E × List E  List E × List E
    enqueue-⊤ e (l₁ , l₂) = e  l₁ , l₂

    enqueue-abs : E  List E  List E
    enqueue-abs = listPreQueue .enqueue

    enqueue-correct : revAppend  enqueue-⊤ e  enqueue-abs e  revAppend
    enqueue-correct = funExt λ { (l₁ , l₂)  sym (++-assoc l₂ (rev l₁) (e  [])) }

batchedPreQueue .dequeue x =
  glue (square (fst  dequeue-⊤) (fst  dequeue-abs) fst-dequeue-correct x) ,
  square (snd  dequeue-⊤) (snd  dequeue-abs) snd-dequeue-correct x
  where
    dequeue-⊤ : List E × List E  E × (List E × List E)
    dequeue-⊤ (l₁ , []) with rev l₁
    ... | [] = e₀ , [] , []
    ... | e  l₂ = e , [] , l₂
    dequeue-⊤ (l₁ , e  l₂) = e , l₁ , l₂

    dequeue-abs : List E  E × List E
    dequeue-abs = listPreQueue .dequeue

    fst-dequeue-correct : fst  dequeue-⊤  (fst  dequeue-abs)  revAppend
    fst-dequeue-correct = funExt lemma
      where
        lemma : (x : List E × List E)  fst (dequeue-⊤ x)  fst (dequeue-abs (revAppend x))
        lemma (l₁ , []) with rev l₁
        ... | [] = refl
        ... | e  l₂ = refl
        lemma (l₁ , e  l₂) = refl

    snd-dequeue-correct : revAppend  (snd  dequeue-⊤)  (snd  dequeue-abs)  revAppend
    snd-dequeue-correct = funExt lemma
      where
        lemma : (x : List E × List E)  revAppend (snd (dequeue-⊤ x))  snd (dequeue-abs (revAppend x))
        lemma (l₁ , []) with rev l₁
        ... | [] = refl
        ... | e  l₂ = ++-unit-r l₂
        lemma (l₁ , e  l₂) = refl


batchedQueue : Queue
batchedQueue .fst = batchedPreQueue
batchedQueue .snd abs =
  prequeue-path
    (◯Glue' revAppend abs)
    refl
     e q 
        equivFun (◯Glue' revAppend abs) (batchedPreQueue .enqueue e q)
      ≡⟨ refl 
        transport refl (q .snd .fst (transport refl abs) ++ [ e ])
      ≡⟨ transportRefl _ 
        q .snd .fst (transport refl abs) ++ [ e ]
      ≡⟨ sym (cong (_++ [ e ]) (transportRefl _)) 
        transport refl (q .snd .fst (transport refl abs)) ++ [ e ]
      ≡⟨ refl 
        listPreQueue .enqueue e (◯Glue' revAppend abs .fst q)
      
    )
     q 
        glue (square _ (fst  listPreQueue .dequeue) _ q)
      ≡⟨ ◯glue (square _ (fst  listPreQueue .dequeue) _ q) abs 
        fst (listPreQueue .dequeue (q .snd .fst abs))
      ≡⟨ cong  x  fst (listPreQueue .dequeue (q .snd .fst x))) (sym (transportRefl abs)) 
        fst (listPreQueue .dequeue (q .snd .fst (transport refl abs)))
      ≡⟨ cong (fst  listPreQueue .dequeue) (sym (transportRefl (q .snd .fst (transport refl abs)))) 
        fst (listPreQueue .dequeue (transport refl (q .snd .fst (transport refl abs))))
      ≡⟨ refl 
        fst (listPreQueue .dequeue (◯Glue' revAppend abs .fst q))
      
    )
     q 
        equivFun (◯Glue' revAppend abs) (snd (batchedPreQueue .dequeue q))
      ≡⟨ refl 
        transport refl (snd (listPreQueue .dequeue (q .snd .fst (transport refl abs))))
      ≡⟨ transportRefl _ 
        snd (listPreQueue .dequeue (q .snd .fst (transport refl abs)))
      ≡⟨ sym (cong {y = q .snd .fst _}  x  snd (listPreQueue .dequeue x)) (transportRefl _)) 
        snd (listPreQueue .dequeue (transport refl (q .snd .fst (transport refl abs))))
      ≡⟨ refl 
        snd (listPreQueue .dequeue (◯Glue' revAppend abs .fst q))
      
    )