module LabelExpr.LabelExpr where

open import Data.Nat
open import Data.Unit using (; tt)
open import Data.Bool using (true; false) renaming (Bool to 𝔹)
open import Data.List hiding ([_])
open import Data.Product renaming (_,_ to ⟨_,_⟩)
open import Data.Sum using (_⊎_)
open import Data.Maybe
open import Relation.Nullary using (¬_; Dec; yes; no; recompute)
open import Relation.Nullary.Negation using (contradiction; ¬?)
open import Relation.Binary.PropositionalEquality using (_≡_; _≢_; refl; cong; sym)
open import Function using (case_of_)

open import Common.Utils
open import Common.SecurityLabels
open import Common.BlameLabels
open import CoercionExpr.CoercionExpr
  renaming (_∎ to _∎ₗ ; _—→⟨_⟩_ to _—→ₗ⟨_⟩_; ↠-trans to ↠-transₗ)
  hiding (Progress; progress; plug-cong)
open import CoercionExpr.SyntacComp
open import CoercionExpr.Precision renaming (prec→⊑ to precₗ→⊑)
open import CoercionExpr.SecurityLevel renaming (∥_∥ to ∥_∥ₗ)


data LExpr : Set where

  l : StaticLabel  LExpr

  _⟪_⟫ :  {g₁ g₂}  LExpr  CExpr g₁  g₂  LExpr

  blame : BlameLabel  LExpr


data Irreducible :  {g₁ g₂} ( : CExpr g₁  g₂)  Set where

  ir :  {g₁ g₂} { : CExpr g₁  g₂}
     CVal 
     .(g₁  g₂)
     Irreducible 


data LVal : LExpr  Set where

  {- raw value -}
  v-l :  {}  LVal (l )

  {- wrapped value (one cast) -}
  v-cast :  { g} { : CExpr l   g}
     Irreducible 
     LVal (l    )

data LResult : LExpr  Set where

  success :  {V}  LVal V  LResult V

  fail :  {p}  LResult (blame p)


data ⊢_⇐_ : LExpr  Label  Set where

  ⊢l :  {}   l   l 

  ⊢cast :  {g₁ g₂} {M} { : CExpr g₁  g₂}
      M  g₁
      ------------------
      M     g₂

  ⊢blame :  {g} {p}   blame p  g


infix 2 _—→ₑ_

data _—→ₑ_ : (M N : LExpr)  Set where

  ξ :  {g₁ g₂} {M N} { : CExpr g₁  g₂}
     M —→ₑ N
      --------------------------
     M    —→ₑ N   

  ξ-blame :  {g₁ g₂} { : CExpr g₁  g₂} {p}
      -----------------------------------------------
     blame p    —→ₑ blame p

  β-id :  {}  l   id (l )  —→ₑ l 

  cast :  { g} { c̅ₙ : CExpr l   g}
      —→⁺ c̅ₙ
     CVal c̅ₙ
      -------------------------------
     l     —→ₑ l   c̅ₙ 

  blame :  { g} { : CExpr l   g} {p}
      —↠  (l ) g p
      --------------------------
     l     —→ₑ blame p

  comp :  { g₁ g₂} {c̅ᵢ : CExpr l   g₁} { : CExpr g₁  g₂}
     Irreducible c̅ᵢ
      -------------------------------------------
     l   c̅ᵢ     —→ₑ l   c̅ᵢ   



data Progress : LExpr  Set where

  done :  {M}  LVal M  Progress M

  error :  {p}  Progress (blame p)

  step :  {M N}  M  —→ₑ N  Progress M

progress :  {g M}   M  g  Progress M
progress ⊢l = done v-l
progress (⊢cast { = } ⊢M) =
  case progress ⊢M of λ where
  (done v) 
    case  v , ⊢M  of λ where
     v-l , ⊢l  
      case cexpr-sn  of λ where
        , _ ∎ₗ , success id   step β-id
        , _ ∎ₗ , success (inj 𝓋)   done (v-cast (ir (inj 𝓋) λ ()))
        , _ ∎ₗ , success (up id)   done (v-cast (ir (up id) λ ()))
        , _ —→ₗ⟨ c̅→c̅′  c̅′↠d̅ , success 𝓋   step (cast (_ —→ₗ⟨ c̅→c̅′  c̅′↠d̅) 𝓋)
       _ , c̅↠⊥ , fail        step (blame c̅↠⊥)
     v-cast { = c̅′} i , ⊢cast _   step (comp i)
  (error)  step ξ-blame
  (step M→N)  step (ξ M→N)
progress ⊢blame = error

preserve :  {g M N}
    M  g
   M —→ₑ N
    N  g
preserve (⊢cast ⊢M) (ξ M→N) = ⊢cast (preserve ⊢M M→N)
preserve (⊢cast ⊢M) ξ-blame = ⊢blame
preserve (⊢cast ⊢M) β-id = ⊢l
preserve (⊢cast ⊢M) (cast x x₁) = ⊢cast ⊢l
preserve (⊢cast ⊢M) (blame _) = ⊢blame
preserve (⊢cast ⊢M) (comp _) = ⊢cast ⊢l

open import Common.MultiStep   {tt tt  LExpr}) _—→ₑ_ public
  renaming (_—↠_ to _—↠ₑ_; ↠-trans to ↠ₑ-trans)

plug-congₑ :  {g₁ g₂} {M N : LExpr} { : CExpr g₁  g₂}
   M —↠ₑ N
   M    —↠ₑ N   
plug-congₑ (M ) = (M  _ ) 
plug-congₑ (M —→⟨ M→L  L↠N) = M  _  —→⟨ ξ M→L  (plug-congₑ L↠N)

preserve-mult :  {g} {M N}   M  g  M —↠ₑ N   N  g
preserve-mult ⊢M (_ ) = ⊢M
preserve-mult ⊢L (L —→⟨ L→M  M↠N) = preserve-mult (preserve ⊢L L→M) M↠N


data ⊢_⊑_⇐_⊑_ :  (M M′ : LExpr) (g₁ g₂ : Label)  Set where

  ⊑-l :  {}   l   l   l   l 

  ⊑-cast :  {g₁ g₁′ g₂ g₂′} {M M′}
             { : CExpr g₁  g₂} {c̅′ : CExpr g₁′  g₂′}
      M  M′  g₁  g₁′
        c̅′
      --------------------------------------
      M     M′  c̅′   g₂  g₂′

  ⊑-castl :  {g₁ g₂ g′} {M M′} { : CExpr g₁  g₂}
      M  M′  g₁  g′
     ⊢l   g′
      --------------------------------------
      M     M′  g₂  g′

  ⊑-castr :  {g g₁′ g₂′} {M M′} {c̅′ : CExpr g₁′  g₂′}
      M  M′  g  g₁′
     ⊢r g  c̅′
      --------------------------------------
      M  M′  c̅′   g  g₂′

  ⊑-blame :  {g g′} {M} {p}
      M  g
     g ⊑ₗ g′
      --------------------------
      M  blame p  g  g′


{- Precision implies that both sides are well-typed -}
prec→⊢ :  {g g′} {M M′}
    M  M′  g  g′
    M  g  ×   M′  g′
prec→⊢ ⊑-l =  ⊢l , ⊢l 
prec→⊢ (⊑-cast M⊑M′ c̅⊑c̅′) =
  let  ⊢M , ⊢M′  = prec→⊢ M⊑M′ in
   ⊢cast ⊢M , ⊢cast ⊢M′ 
prec→⊢ (⊑-castl M⊑M′ _) =
  let  ⊢M , ⊢M′  = prec→⊢ M⊑M′ in
   ⊢cast ⊢M , ⊢M′ 
prec→⊢ (⊑-castr M⊑M′ _) =
  let  ⊢M , ⊢M′  = prec→⊢ M⊑M′ in
   ⊢M , ⊢cast ⊢M′ 
prec→⊢ (⊑-blame ⊢M _) =  ⊢M , ⊢blame 


{- Term precision implies type precision -}
prec→⊑ :  {g₁ g₂} {M N}   M  N  g₁  g₂  g₁ ⊑ₗ g₂
prec→⊑ ⊑-l = l⊑l
prec→⊑ (⊑-cast _ c̅⊑c̅′)   = proj₂ (precₗ→⊑ _ _ c̅⊑c̅′)
prec→⊑ (⊑-castl _ c̅⊑g′)  = proj₂ (prec-left→⊑ _ c̅⊑g′)
prec→⊑ (⊑-castr _ g⊑c̅′)  = proj₂ (prec-right→⊑ _ g⊑c̅′)
prec→⊑ (⊑-blame ⊢M g⊑g′) = g⊑g′


{- Precision of label expressions implies the precision of coercion expressions -}
prec-inv :  { ℓ′ g g′} { : CExpr l   g} {c̅′ : CExpr l ℓ′  g′}
    l      l ℓ′  c̅′   g  g′
     ℓ′  ×     c̅′
prec-inv (⊑-cast ⊑-l c̅⊑c̅′)                 =  refl , c̅⊑c̅′ 
prec-inv (⊑-castl (⊑-castr ⊑-l ℓ⊑c̅′) c̅⊑g′) =  refl , comp-pres-⊑-rl ℓ⊑c̅′ c̅⊑g′ 
prec-inv (⊑-castr (⊑-castl ⊑-l c̅⊑ℓ) g⊑c̅′)  =  refl , comp-pres-⊑-lr c̅⊑ℓ g⊑c̅′  


{- Security level -}
∥_∥ :  (V : LExpr)  LVal V  StaticLabel
 l         v-l                = 
 l      (v-cast (ir 𝓋 _)) =   ∥ₗ 𝓋



lexpr-sn :  {A} L
    L  A
    ----------------------------------------
   ∃[ M ] (L —↠ₑ M) × LResult M
lexpr-sn (l ) ⊢l =  l  , _  , success v-l 
lexpr-sn (L   ) (⊢cast ⊢L) =
  case lexpr-sn L ⊢L of λ where
   blame p , L↠blame , fail  
     blame p , ↠ₑ-trans (plug-congₑ L↠blame) (_ —→⟨ ξ-blame  _ ) ,
      fail 
   l  , L↠V , success v-l  
    case  preserve-mult ⊢L L↠V , cexpr-sn   of λ where
     ⊢l ,  _ _ q , c̅↠d̅ , fail  
       blame q , ↠ₑ-trans (plug-congₑ L↠V)
                            (_ —→⟨ blame c̅↠d̅  _ ) ,
        fail 
     ⊢l , c̅ₙ , _ ∎ₗ , success id  
       l  , ↠ₑ-trans (plug-congₑ L↠V)
                        (_ —→⟨ β-id  _ ) ,
        success v-l 
     ⊢l , c̅ₙ , _ —→ₗ⟨ c̅→d̅  d̅↠c̅ₙ , success id  
       l  , ↠ₑ-trans (plug-congₑ L↠V)
                        (_ —→⟨ cast (_ —→ₗ⟨ c̅→d̅  d̅↠c̅ₙ) id  _ —→⟨ β-id  _ ) ,
        success v-l 
     ⊢l , c̅ₙ , _ ∎ₗ , success (up id)  
       l   _  , plug-congₑ L↠V ,
        success (v-cast (ir (up id)  ()))) 
     ⊢l , c̅ₙ , _ —→ₗ⟨ c̅→d̅  d̅↠c̅ₙ , success (up id)  
       l   _  , ↠ₑ-trans (plug-congₑ L↠V)
                              (_ —→⟨ cast (_ —→ₗ⟨ c̅→d̅  d̅↠c̅ₙ) (up id)  _ ) ,
        success (v-cast (ir (up id)  ()))) 
     ⊢l , c̅ₙ , _ ∎ₗ , success (inj 𝓋)  
       l   _  , plug-congₑ L↠V ,
        success (v-cast (ir (inj 𝓋)  ()))) 
     ⊢l , c̅ₙ , _ —→ₗ⟨ c̅→d̅  d̅↠c̅ₙ , success (inj 𝓋)  
       l   _  , ↠ₑ-trans (plug-congₑ L↠V)
                              (_ —→⟨ cast (_ —→ₗ⟨ c̅→d̅  d̅↠c̅ₙ) (inj 𝓋)  _ ) ,
        success (v-cast (ir (inj 𝓋)  ()))) 
   l   c̅ᵢ  , L↠V , success (v-cast i)  
    case preserve-mult ⊢L L↠V of λ where
    (⊢cast ⊢l) 
      case cexpr-sn (c̅ᵢ  ) of λ where
        _ _ q , c̅↠d̅ , fail  
         blame q , ↠ₑ-trans (plug-congₑ L↠V)
                    (_ —→⟨ comp i  _ —→⟨ blame c̅↠d̅  _ ) ,
          fail 
       c̅ₙ , c̅↠d̅ , success id  
         l  , ↠ₑ-trans (plug-congₑ L↠V)
                (_ —→⟨ comp i  _ —→⟨ cast (comp-→⁺ c̅↠d̅ id) id  _ —→⟨ β-id  _ ) ,
          success v-l 
       c̅ₙ , c̅↠c̅ₙ , success (up id)  
         l   _  , ↠ₑ-trans (plug-congₑ L↠V)
                      (_ —→⟨ comp i  _ —→⟨ cast (comp-→⁺ c̅↠c̅ₙ (up id)) (up id)  _ ) ,
          success (v-cast (ir (up id)  ()))) 
       c̅ₙ , c̅↠c̅ₙ , success (inj 𝓋)  
         l   _  , ↠ₑ-trans (plug-congₑ L↠V)
                      (_ —→⟨ comp i  _ —→⟨ cast (comp-→⁺ c̅↠c̅ₙ (inj 𝓋)) (inj 𝓋)  _ ) ,
          success (v-cast (ir (inj 𝓋)  ()))) 
lexpr-sn (blame p) ⊢blame =  blame p , _  , fail 


uniq-LVal :  {V}  (v w : LVal V)  v  w
uniq-LVal v-l v-l = refl
uniq-LVal (v-cast (ir 𝓋 x)) (v-cast (ir 𝓋′ y)) rewrite uniq-CVal 𝓋 𝓋′ = refl


LVal⌿→ :  {V M}  LVal V  ¬ (V —→ₑ M)
LVal⌿→ (v-cast (ir id x)) β-id = contradiction refl (recompute (¬? (_ ==? _)) x)
LVal⌿→ (v-cast (ir 𝓋 _)) (cast (_ —→ₗ⟨ r  _) _) = CVal⌿→ 𝓋 r
LVal⌿→ (v-cast (ir 𝓋 _)) (blame (_ —→ₗ⟨ r  _))  = CVal⌿→ 𝓋 r

LResult⌿→ :  {M N}  LResult M  ¬ (M —→ₑ N)
LResult⌿→ (success v) = LVal⌿→ v


detₑ :  {L M N}
   L —→ₑ M
   L —→ₑ N
    ----------------------------
   M  N
detₑ (ξ L→M) (ξ L→N) = cong _⟪ _  (detₑ L→M L→N)
detₑ (ξ L→M) (comp i) = contradiction L→M (LVal⌿→ (v-cast i))
detₑ ξ-blame ξ-blame = refl
detₑ β-id β-id = refl
detₑ β-id (cast (_ —→ₗ⟨ r  _) _) = contradiction r (CVal⌿→ id)
detₑ β-id (blame (_ —→ₗ⟨ r  _)) = contradiction r (CVal⌿→ id)
detₑ (cast (_ —→ₗ⟨ r  _) _) β-id = contradiction r (CVal⌿→ id)
detₑ (cast c̅→⁺d̅₁ 𝓋₁) (cast c̅→⁺d̅₂ 𝓋₂) =
  cong (_ ⟪_⟫) (det-mult (→⁺-impl-↠ c̅→⁺d̅₁) (→⁺-impl-↠ c̅→⁺d̅₂)
                         (success 𝓋₁) (success 𝓋₂))
detₑ (cast c̅→⁺c̅ₙ 𝓋) (blame c̅↠⊥)
  with det-mult (→⁺-impl-↠ c̅→⁺c̅ₙ) c̅↠⊥ (success 𝓋) fail
... | refl = case 𝓋 of λ ()
detₑ (blame (_ —→ₗ⟨ r  _)) β-id = contradiction r (CVal⌿→ id)
detₑ (blame c̅↠⊥) (cast c̅→⁺c̅ₙ 𝓋)
  with det-mult (→⁺-impl-↠ c̅→⁺c̅ₙ) c̅↠⊥ (success 𝓋) fail
... | refl = case 𝓋 of λ ()
detₑ (blame c̅↠⊥₁) (blame c̅↠⊥₂)
  with det-mult c̅↠⊥₁ c̅↠⊥₂ fail fail
... | refl = refl
detₑ (comp i) (ξ L→N) = contradiction L→N (LVal⌿→ (v-cast i))
detₑ (comp _) (comp _) = refl


det-multₑ :  {M V W}
   M —↠ₑ V
   M —↠ₑ W
   LResult V  LResult W
    -----------------------
   V  W
det-multₑ (V ) (W ) _ _ = refl
det-multₑ (_ ) (_ —→⟨ r  ↠W) v = contradiction r (LResult⌿→ v)
det-multₑ (_ —→⟨ r  ↠V) (_ ) _ v = contradiction r (LResult⌿→ v)
det-multₑ (L —→⟨ L→M  M↠V) (L —→⟨ L→N  N↠W) v w
  with detₑ L→M L→N
... | refl = det-multₑ M↠V N↠W v w

cast-red-label-eq :  { ℓ′ g} { : CExpr l   g} {c̅′ : CExpr l ℓ′  g}
   l     —↠ₑ l ℓ′  c̅′ 
    ------------------------------------
     ℓ′
cast-red-label-eq (l     ) = refl
cast-red-label-eq (_ —→⟨ β-id  (_ —→⟨ r  _)) =
  contradiction r (LVal⌿→ v-l)
cast-red-label-eq (_ —→⟨ cast _ _  r*) = cast-red-label-eq r*
cast-red-label-eq (_ —→⟨ blame _  _ —→⟨ r  _) =
  contradiction r (LResult⌿→ fail)

cast-red-inv :  { g} {  : CExpr l   g}
   l     —↠ₑ l    
    --------------------------------------------
    —↠ 
cast-red-inv (_ ) = _ ∎ₗ
cast-red-inv (_ —→⟨ β-id  _ —→⟨ r  _) =
  contradiction r (LVal⌿→ v-l)
cast-red-inv (_ —→⟨ cast c̅→⁺c̅ₙ v  r*) =
  ↠-transₗ (→⁺-impl-↠ c̅→⁺c̅ₙ) (cast-red-inv r*)
cast-red-inv (_ —→⟨ blame _  _ —→⟨ r  _) =
  contradiction r (LResult⌿→ fail)

cast-to-label-inv :  { ℓ′ g} { : CExpr l   g}
   l     —↠ₑ l ℓ′
    ------------------------------------
   (  ℓ′) × (l     —↠ₑ l   id (l ) )
cast-to-label-inv (l   id (l _)  —→⟨ β-id  _ ) =  refl , _  
cast-to-label-inv (l _  _  —→⟨ cast r 𝓋  r*) =
  let  eq , ih  = cast-to-label-inv r* in
   eq , _ —→⟨ cast r 𝓋  ih 
cast-to-label-inv (l _  _  —→⟨ blame _  _ —→⟨ r  _) =
  contradiction r (LResult⌿→ fail)

cast-id-id :  {g} {V}
   LVal V
    V  g
   V  id g  —↠ₑ V
cast-id-id v-l ⊢l = _ —→⟨ β-id  _ 
cast-id-id (v-cast (ir 𝓋 x)) (⊢cast ⊢l) =
  _ —→⟨ comp (ir 𝓋 x)  _ —→⟨ cast (_ —→ₗ⟨ id 𝓋  _ ∎ₗ) 𝓋  _