{-# OPTIONS --no-positivity-check --allow-unsolved-metas #-}

open import Algebra
open import Prelude
open import Relation.Binary
open import WellFounded
open import Algebra.Monus
open import Data.Maybe

module Control.Comonad.Stepped {s} {𝑆 : Type s} {𝑊 : 𝑆  Type s  Type s}
  (mon : TMAPOM 𝑆)
  (comon : GradedComonad (TMAPOM.monoid mon) 𝑊)
  {𝐹 : Type s  Type s}
  (functor : Functor 𝐹)
  where

open TMAPOM mon
open GradedComonad comon renaming (map to cmap)
open Functor functor renaming (map to fmap)

CofreeF : Type s  𝑆  Type s  Type s
CofreeF A w B = 𝑊 w (A × 𝐹 B)

functorCofreeF :  {w}  Functor (CofreeF A w)
functorCofreeF .Functor.map f = cmap (map₂ (fmap f))
functorCofreeF .Functor.map-id = {!!}
functorCofreeF .Functor.map-comp = {!!}

module _ {A : Type s} where
  open import Codata.SegFix commutativeMonoid (CofreeF A) (functorCofreeF {A = A}) public using (Fix; unfold)

Cofree⁺ : Type s  𝑆  Type s
Cofree⁺ A = Fix {A = A}

Cofree : Type s  Type s
Cofree A = Cofree⁺ A ε

private variable u v w : 𝑆

traceT-alg : (𝑊 ε A  B)  (𝑊 ε A  𝐹 ( v × (v  ε) × 𝑊 v A))  𝑊 w A  CofreeF B w ( v × (v  ε) × 𝑊 v A)
traceT-alg f c r = r =>>[ ∙ε _ ] λ rs  f rs , c rs

module _ (wf : WellFounded _≺_) where

  traceT : (𝑊 ε A  B)  (𝑊 ε A  𝐹 ( v × (v  ε) × 𝑊 v A))  𝑊 w A  Cofree⁺ B w
  traceT f c = unfold wf (flip 𝑊 _) (traceT-alg f c)


--   extend-alg : (Cofree A → B) → Cofree⁺ A w → CofreeF B w (∃ v × (v ≢ ε) × Cofree⁺ A v)
--   extend-alg {w = w} f xs = xs =>>[ ∙ε _ ] (λ ys → f ys , fmap (λ k → fst (k {i = w}) , {!!} , cmap {!!} (snd (k {i = w}))) (snd (extract ys)))

--   extend : (Cofree A → B) → Cofree⁺ A w → Cofree⁺ B w
--   extend f = unfold wf (Cofree⁺ _) (extend-alg f)


-- Weighted : (𝑆 → Type a → Type b) → Type a → Type (s ℓ⊔ b)
-- Weighted C A = ∃ w × C w A


-- module OnFunctor (functor : Functor s s) where
--   open Functor functor renaming (map to fmap)

--   record Cofree⁺ (w : 𝑆) (A : Type s) : Type s where
--     constructor ⟪_⟫
--     field step : 𝑊 w (A × 𝐹 (Weighted Cofree⁺ A))
--   open Cofree⁺ public

--   Cofree : Type s → Type s
--   Cofree = Cofree⁺ ε

--   extend : (Cofree A → B) → Cofree⁺ w A → Cofree⁺ w B
--   extend f x .step = x .step =>>[ ∙ε _ ] λ ys → f ⟪ ys ⟫ , fmap (map₂ (extend f)) (snd (extract ys)) 

--   extract′ : Cofree A → A
--   extract′ = fst ∘ extract ∘ step


--   iterT : (𝑊 ε A → 𝐹 (Weighted 𝑊 A)) → 𝑊 ε A → Cofree A
--   iterT = traceT extract

-- module AsHeap (_<*>_ : ∀ {w A B} → 𝑊 w (A → B) → 𝑊 w A → 𝑊 w B) where
--   open import Data.List.Properties using (listFunctor)
--   open import Data.List using (List; _∷_; [])
--   open OnFunctor listFunctor
--   Heap : Type s → Type s
--   Heap = Weighted Cofree⁺ 

--   _∪_ : Heap A → Heap A → Heap A
--   (xʷ , xs) ∪ (yʷ , ys) with xʷ ≤|≥ yʷ
--   ... | inl (k , y≡x∙k) = xʷ , ⟪ (step ys =>>[ sym y≡x∙k ] λ { y (x , xs) → x , ((k , ⟪ y ⟫) ∷ xs)}) <*> step xs ⟫
--   ... | inr (k , x≡y∙k) = yʷ , ⟪ (step xs =>>[ sym x≡y∙k ] λ { x (y , ys) → y , ((k , ⟪ x ⟫) ∷ ys)}) <*> step ys ⟫

--   merge⁺ : Heap A → List (Heap A) → Heap A
--   merge⁺ x [] = x
--   merge⁺ x₁ (x₂ ∷ []) = x₁ ∪ x₂
--   merge⁺ x₁ (x₂ ∷ x₃ ∷ xs) = (x₁ ∪ x₂) ∪ merge⁺ x₃ xs

--   merge : List (Heap A) → Maybe (Heap A)
--   merge [] = nothing
--   merge (x ∷ xs) = just (merge⁺ x xs)

  
--   open import Data.Maybe.Properties using (maybeFunctor)
--   open import Data.Maybe using (mapMaybe)

--   module L = OnFunctor maybeFunctor

--   search : Cofree⁺ w A → L.Cofree⁺ w A
--   search = L.⟪_⟫ ∘ map (map₂ (mapMaybe (map₂ search) ∘ merge)) ∘ step
  


-- -- data Heap (A : Type s) : Type s where
-- --   _◃_ : (w : 𝑆) → (xs : 𝐹 w (A × List (Heap A))) → Heap A

-- -- extend : (Heap A → B) → Heap A → Heap B
-- -- extend f (w ◃ xs) = w ◃ (xs =>>[ ∙ε w ] (λ ys → f (ε ◃ ys) , Lmap (extend f) (snd (extract ys))))

-- -- module _ (2-monoid : ∀ {A B w} → 𝐹 w A → 𝐹 w B → 𝐹 w (A × B)) where
-- --   _∪_ : Heap A → Heap A → Heap A
-- --   (xw ◃ xs) ∪ (yw ◃ ys) with xw ≤|≥ yw
-- --   ... | inl (k , p) = xw ◃ map (λ { (y , (x , xs)) → x , (k ◃ y) ∷ xs }) (2-monoid (ys =>>[ sym p ] id) xs)
-- --   ... | inr (k , p) = yw ◃ map (λ { (x , (y , ys)) → y , (k ◃ x) ∷ ys }) (2-monoid (xs =>>[ sym p ] id) ys)

-- -- -- mutual
-- -- --   record Heap (A : Type a) : Type (s ℓ⊔ a) where
-- -- --     inductive; constructor _◃_
-- -- --     field
-- -- --       hd : A
-- -- --       tl : Next A

-- -- --   record Next {a} (A : Type a) : Type (s ℓ⊔ a) where
-- -- --     coinductive; constructor ⟪_⟫
-- -- --     field next : Span A

-- -- --   data Span {a} (A : Type a) : Type (s ℓ⊔ a) where
-- -- --     [] : Span A
-- -- --     until : (s : 𝑆) → (s≢ε : s ≢ ε) → (xs : Heap A) → Span A
-- -- -- open Heap public
-- -- -- open Next public

-- -- -- State : Type a → Type _
-- -- -- State A = 𝑆 → A × 𝑆

-- -- -- pop′ : (s : 𝑆) → Acc _<_ s → Heap A → A × 𝑆
-- -- -- pop′ s₂ a xs with xs .tl .next
-- -- -- pop′ s₂ a xs | [] = xs .hd , ε
-- -- -- pop′ s₂ a xs | until s₁ s₁≢ε ys with s₁ ≤? s₂
-- -- -- pop′ s₂ a xs | until s₁ s₁≢ε ys | no s₁≰s₂ = xs .hd , fst (<⇒≤ s₁≰s₂)
-- -- -- pop′ s₂ (acc wf) xs | until s₁ s₁≢ε ys | yes (k₁ , s₂≡s₁∙k₁) = pop′ k₁ (wf k₁ lemma) ys
-- -- --   where
-- -- --   lemma : k₁ < s₂
-- -- --   lemma (k₂ , k₁≡s₂∙k₂) = s₁≢ε (zeroSumFree s₁ k₂ (sym (cancel k₁ _ _ p)))
-- -- --     where
-- -- --     p : k₁ ∙ ε ≡ k₁ ∙ (s₁ ∙ k₂)
-- -- --     p = ∙ε k₁ ; k₁≡s₂∙k₂ ; cong (_∙ k₂) s₂≡s₁∙k₁ ; cong (_∙ k₂) (comm s₁ k₁) ; assoc k₁ s₁ k₂

-- -- -- pop : Heap A → State A
-- -- -- pop xs s = pop′ s (wf s) xs

-- -- -- mutual
-- -- --   stepFrom : State A → (s : 𝑆) → Dec (s ≡ ε) → Span A
-- -- --   stepFrom f s (yes p) = []
-- -- --   stepFrom f s (no ¬p) = until s ¬p (tabulate (f ∘ (s ∙_)))

-- -- --   tabulate : State A → Heap A
-- -- --   tabulate f =
-- -- --     let x , s = f ε
-- -- --     in x ◃ λ where .next → stepFrom f s (s ≟ ε)

-- -- -- pop-ε : (xs : Heap A) (a : Acc _<_ ε) → pop′ ε a xs .fst ≡ xs .hd
-- -- -- pop-ε xs _ with xs .tl .next
-- -- -- pop-ε xs _ | [] = refl
-- -- -- pop-ε xs _ | until s s≢ε ys with s ≤? ε
-- -- -- pop-ε xs _ | until s s≢ε ys | no  s≰ε = refl
-- -- -- pop-ε xs _ | until s s≢ε ys | yes s≤ε = ⊥-elim (s≢ε (antisym s≤ε (positive s)))

-- -- -- -- slide : Heap A → Heap A
-- -- -- -- slide xs with xs .tl .next
-- -- -- -- slide xs | [] = xs
-- -- -- -- slide xs | [] = []

-- -- -- -- pop-tl : ∀ (x : A) s₁ (s₁≢ε : s₁ ≢ ε) xs s₂ → pop (x ◃ ⟪ until s₁ s₁≢ε xs ⟫) (s₁ ∙ s₂) ≡ pop xs s₂
-- -- -- -- pop-tl x s₁ s₁≢ε xs s₂ with s₁ ≤? (s₁ ∙ s₂)
-- -- -- -- pop-tl x s₁ s₁≢ε xs s₂ | no  s₁≰s₁∙s₂ = ⊥-elim (s₁≰s₁∙s₂ (s₂ , refl))
-- -- -- -- pop-tl x s₁ s₁≢ε xs s₂ | yes (k , s₁≤s₁∙s₂) =
-- -- -- --   let p = cancel s₁ s₂ k s₁≤s₁∙s₂
-- -- -- --   in {!!} ; cong (λ w → pop′ s₂ w xs) (isPropAcc {!!} (wf s₂))

-- -- -- -- seg-leftInv′ : (x : Heap A) → tabulate (pop x) ≡ x
-- -- -- -- seg-leftInv′ x = {!!}

-- -- -- -- mutual
-- -- -- --   seg-leftInv′ : (xs : Heap A) → stepFrom (pop xs) (pop xs ε .snd) (pop xs ε .snd ≟ ε) ≡ xs .tl .next
-- -- -- --   seg-leftInv′ (x ◃ xs) with pop (x ◃ xs) ε .snd ≟ ε
-- -- -- --   seg-leftInv′ (x ◃ xs) | yes s≡ε = {!!}
-- -- -- --   seg-leftInv′ (x ◃ xs) | no  s≢ε = {!!}

-- -- -- --   seg-leftInv : (x : Heap A) → tabulate (pop x) ≡ x
-- -- -- --   seg-leftInv (x ◃ xs) i .hd = pop-ε (x ◃ xs) (wf ε) i
-- -- -- --   seg-leftInv (x ◃ xs) i .tl .next = seg-leftInv′ (x ◃ xs) i

-- -- -- -- state-iso : Heap A ⇔ State A
-- -- -- -- state-iso .fun = pop
-- -- -- -- state-iso .inv = tabulate
-- -- -- -- state-iso .rightInv = {!!}
-- -- -- -- state-iso .leftInv  = {!!}