{-# OPTIONS --postfix-projections --safe --without-K #-}

open import Level using (lift; lower)
open import Data.Product using (_,_; proj₁; proj₂)
open import Relation.Binary.Construct.Closure.ReflexiveTransitive using (ε; _◅_; _◅◅_)

module MAUVE.CutElim {a} (Atom : Set a) where

open import MAUVE.Structure Atom
open import MAUVE.Base Atom as MAUVE
import MAUVE.Symmetric Atom as SMAUVE
open import MAUVE.Frame
open FrameModel MAUVE.frame
  using
    ( Chu
    ; module L
    ; module I
    ; module C
    ; embed
    )
  renaming
    ( model to analyticModel
    )
open C using (Chu; pos; neg; int; _≤_; fpos; fneg)
open import MAUVE.Interpretation Atom analyticModel  A  embed (`- A))

interactᴾ : (P Q : Structure)  (I.U (I.η Q) L.⊸ I.U I.ι) L.⅋ L.η (P `⊗ Q) L.≤ L.η P
interactᴾ P Q .L.*≤* {x} (y , z , x≤y⅋z , ϕ₁ , lift z≤P⊗Q) =
  lift (x≤y⅋z
        ◅◅ (`⅋⟩⋆ z≤P⊗Q)
        ◅◅ (bwd `⅋-comm  ε)
        ◅◅ (step `switch  ε)
        ◅◅ (`⊗⟩⋆ ((bwd `⅋-comm  ε) ◅◅ (ϕ₁ {Q} ((I.leaf Q (lift ε)) , ε)) .lower))
        ◅◅ fwd `⊗-identityʳ  ε)

interact : (P Q : Structure)  (I.η Q I.⊸ I.ι) I.⅋ I.η (P `⊗ Q) I.≤ I.η P
interact P Q =
    I.≤-trans (I.⅋-mono I.counit⁻¹ I.≤-refl)
    (I.≤-trans (I.α-monoidal .proj₁)
    (I.α-mono (L.≤-trans (L.⅋-mono I.U⊸ L.≤-refl) (interactᴾ P Q))))

interact-!ᴸ :  P  (L.! (L.η P L.⊸ I.U I.ι) L.⅋ L.η (`! P)) L.≤ L.ε
interact-!ᴸ P .L.*≤* {P'} (Q , R , P'⟶⋆QR , (Q' , Q≤Q' , ?Q' , ϕ) , lift R≤!P)  =
  lift P'⟶⋆I
  where
    promote :  {S T}  L.!-context T  `! S `⅋ T ⟶⋆ `! (S `⅋ T)
    promote L.nil = (fwd `⅋-identityʳ  ε) ◅◅ (`!⟩⋆ (bwd `⅋-identityʳ  ε))
    promote (L.pair c d) = (bwd `⅋-assoc  ε) ◅◅ (promote c `⟨⅋⋆) ◅◅ promote d ◅◅ (`!⟩⋆ (fwd `⅋-assoc  ε))
    promote L.leaf = (`⅋⟩⋆ (step   ε)) ◅◅ (step `p  ε)

    P'⟶⋆I : P' ⟶⋆ `I
    P'⟶⋆I = P'⟶⋆QR
         ◅◅ (Q≤Q' `⟨⅋⋆)
         ◅◅ (`⅋⟩⋆ R≤!P)
         ◅◅ (fwd `⅋-comm  ε)
         ◅◅ promote ?Q'
         ◅◅ (`!⟩⋆ (fwd `⅋-comm  ε))
         ◅◅ (`!⟩⋆ (ϕ {P} (lift ε) .lower))
         ◅◅ step `e  ε

interact-! :  P  (I.! (I.η P I.⊸ I.ι) I.⅋ I.η (`! P)) I.≤ I.ε
interact-! P =
  I.≤-trans
    (I.α-monoidal .proj₁)
    (I.α-mono (L.≤-trans (L.⅋-mono (L.!-mono (L.≤-trans I.U⊸ help)) L.≤-refl)
                         (interact-!ᴸ P)))
  where help : (I.U (I.η P)) L.⊸ (I.U I.ι) L.≤ L.η P L.⊸ I.U I.ι
        help = L.⊸-residual-to (L.≤-trans (L.⅋-mono I.unit L.≤-refl)
                                          (L.⊸-residual-from L.≤-refl))

mutual
  reflect : (P : Structure)  I.η P I.≤  P  .neg
  reflect `I = I.≤-refl
  reflect `𝟘 = I.⊤-maximum _
  reflect `⊤ = I.η-preserve-⊥ᶜ
  reflect (`+ A) =
    I.⊸-residual-to (I.≤-trans I.η-preserve-∙⁻¹ (I.η-mono ((step `axiom)  ε)))
  reflect (`- A) =
    I.≤-refl
  reflect (P `⅋ Q) =
    I.≤-trans I.η-preserve-∙ (I.⅋-mono (reflect P) (reflect Q))
  reflect (P `⊗ Q) =
    I.∧-greatest
      (I.⊸-residual-to (I.≤-trans (I.⅋-mono (reify Q) I.≤-refl) (I.≤-trans (interact P Q) (reflect P))))
      (I.⊸-residual-to (I.≤-trans (I.⅋-mono (reify P) (I.η-mono (fwd `⊗-comm  ε))) (I.≤-trans (interact Q P) (reflect Q))))
  reflect (P `& Q) =
    I.≤-trans I.η-preserve-∨
      (I.∨-least
        (I.≤-trans (reflect P) I.x≤x∨y)
        (I.≤-trans (reflect Q) I.y≤x∨y))
  reflect (P `⊕ Q) =
    I.∧-greatest
      (I.≤-trans (I.η-mono (step `left  ε)) (reflect P))
      (I.≤-trans (I.η-mono (step `right  ε)) (reflect Q))
  reflect (P `◁ Q) = I.≤-trans I.η-preserve-◁ (I.◁-mono (reflect P) (reflect Q))
  reflect (`? P) =
    I.≤-trans I.η-preserve-! (I.!-mono (reflect P))
  reflect (`! P) =
    I.⊸-residual-to (I.≤-trans (I.⅋-mono (I.!-mono (reify P)) I.≤-refl) (interact-! P))


  reify : (P : Structure)   P  .pos I.≤ I.α (L.η P) I.⊸ I.ι
  reify P = I.⊸-residual-to (I.≤-trans (I.⅋-comm _ _ .proj₁)
                               (I.≤-trans (I.⅋-mono I.≤-refl (reflect P))
                               (I.≤-trans ( P  .int) I.ε≤ι)))

  reify' : (P : Structure)   P  .pos I.≤ I.α (L.η P) I.⊸ I.ε
  reify' P = I.⊸-residual-to (I.≤-trans (I.⅋-comm _ _ .proj₁)
                                (I.≤-trans (I.⅋-mono I.≤-refl (reflect P))
                                ( P  .int)))

main-lemma : (P : Structure)   P   C.¬ (embed P)
main-lemma P .fpos = reify' P
main-lemma P .fneg = reflect P

sem-cut-elim : (P : Structure)  C.ε   P   P ⟶⋆ `I
sem-cut-elim P I≤P = q .I.*≤* (I.leaf P (lift ε) , ε) .lower
  where p : C.ε  C.¬ (embed P)
        p = C.≤-trans I≤P (main-lemma P)
        q : I.η P I.≤ I.ι
        q = I.≤-trans (p .fneg) I.ε≤ι

cut-elim : (P : Structure)  (P SMAUVE.⟶⋆ `I)  P ⟶⋆ `I
cut-elim P prf = sem-cut-elim P  prf ⟧steps