module Dyn.BigStepSimulation 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 using (_×_; ∃-syntax; proj₁; proj₂) renaming (_,_ to ⟨_,_⟩)
open import Data.Maybe
open import Relation.Nullary using (¬_; Dec; yes; no)
open import Relation.Nullary.Negation using (contradiction)
open import Relation.Binary.PropositionalEquality using (_≡_; refl; sym; trans; subst; subst₂)
open import Function using (case_of_)

open import Common.Utils
open import Common.Types using (Base; rep)
open import Common.SecurityLabels
open import Dyn.Syntax
open import Dyn.Values
open import Dyn.BigStep
open import Dyn.BigStepErased
open import Dyn.Erasure
open import Dyn.HeapSecure
open import Dyn.ErasureSubstitution public


sim :  {pc M V μ μ′}
   Secure μ
   μ  pc  M  V  μ′
    ----------------------------------------------------------------------------------
   erase-μ μ  pc  erase M ⇓ₑ erase V  erase-μ μ′
sim sec (⇓-val v) = (⇓ₑ-val (erase-val-value v))
sim {pc} {μ′ = μ′} sec (⇓-app {L = L} {M} {N} {V} {W} { = } L⇓ƛN M⇓V N[V]⇓W) with 
... | low rewrite stamp-val-low (⇓-value N[V]⇓W) | ℓ⋎low≡ℓ {pc} =
  ⇓ₑ-app (sim sec L⇓ƛN) (sim (⇓-pres-sec sec L⇓ƛN) M⇓V) ϵN[ϵV]⇓ϵW
  where
  ϵN[ϵV]⇓ϵW : _  pc  erase N [ erase V ] ⇓ₑ erase W  _
  ϵN[ϵV]⇓ϵW rewrite sym (substitution-erase N V) =
    sim (⇓-pres-sec (⇓-pres-sec sec L⇓ƛN) M⇓V) N[V]⇓W
... | high rewrite erase-stamp-high (⇓-value N[V]⇓W) | ℓ⋎high≡high {pc} =
  ⇓ₑ-app-● (sim sec L⇓ƛN) ϵM⇓ϵV
  where
  ϵμ₂≡ϵμ′ = heap-relate N[V]⇓W
  ϵM⇓ϵV : _  pc  erase M ⇓ₑ erase V  erase-μ μ′
  ϵM⇓ϵV = subst    _  pc  erase M ⇓ₑ erase V  )
             ϵμ₂≡ϵμ′ (sim (⇓-pres-sec sec L⇓ƛN) M⇓V)
sim {pc} {μ = μ} {μ′} sec (⇓-if-true  {μ₁ = μ₁} {L = L} {M} {N} {V} { = } L⇓true  M⇓V)
  with 
... | low  rewrite stamp-val-low (⇓-value M⇓V) | ℓ⋎low≡ℓ {pc} =
  ⇓ₑ-if-true (sim sec L⇓true) (sim (⇓-pres-sec sec L⇓true) M⇓V)
... | high rewrite erase-stamp-high (⇓-value M⇓V) | ℓ⋎high≡high {pc} =
  ⇓ₑ-if-● ϵL⇓●
  where
  ϵμ₁≡ϵμ′ : erase-μ μ₁  erase-μ μ′
  ϵμ₁≡ϵμ′ = heap-relate M⇓V
  ϵL⇓● : erase-μ μ  pc  erase L ⇓ₑ   erase-μ μ′
  ϵL⇓● rewrite sym ϵμ₁≡ϵμ′ = sim sec L⇓true
sim {pc} {μ = μ} {μ′} sec (⇓-if-false {μ₁ = μ₁} {L = L} {M} {N} {V} { = } L⇓false  N⇓V)
  with 
... | low  rewrite stamp-val-low (⇓-value N⇓V) | ℓ⋎low≡ℓ {pc} =
  ⇓ₑ-if-false (sim sec L⇓false) (sim (⇓-pres-sec sec L⇓false) N⇓V)
... | high rewrite erase-stamp-high (⇓-value N⇓V) | ℓ⋎high≡high {pc} =
  ⇓ₑ-if-● ϵL⇓●
  where
  ϵμ₁≡ϵμ′ : erase-μ μ₁  erase-μ μ′
  ϵμ₁≡ϵμ′ = heap-relate N⇓V
  ϵL⇓● : erase-μ μ  pc  erase L ⇓ₑ   erase-μ μ′
  ϵL⇓● rewrite sym ϵμ₁≡ϵμ′ = sim sec L⇓false
sim sec (⇓-ref? {μ} {μ₁} {M = M} {V} { = low} M⇓V fresh pc≼ℓ)
  rewrite erase-μᴸ-length (proj₁ μ₁) =
  ⇓ₑ-ref? goal fresh pc≼ℓ
  where
  goal : erase-μᴸ (proj₁ μ)  _  erase M ⇓ₑ
      erase (stamp-val V (⇓-value M⇓V) low)  erase-μᴸ (proj₁ μ₁)
  goal rewrite stamp-val-low (⇓-value M⇓V) = sim sec M⇓V
sim sec (⇓-ref? { = high} M⇓V fresh pc≼ℓ) =
  ⇓ₑ-ref?-● (sim sec M⇓V)
sim {μ′ =  μᴸ , μᴴ } sec (⇓-deref {v = v} {n = n} { = low} {low} M⇓a eq)
  rewrite stamp-val-low v =
  ⇓ₑ-deref {v = erase-val-value v} (sim sec M⇓a)
    (erase-μ-lookup-low {μᴸ} {μᴴ} {n} {v = v} eq)
sim sec (⇓-deref {v = v} { = low} {high} M⇓a eq)
  rewrite erase-stamp-high v | stamp-val-low v | ⇓-pres-sec sec M⇓a _ _ v eq =
    ⇓ₑ-deref-● (sim sec M⇓a)
sim sec (⇓-deref {v = v} { = high} {low} M⇓a eq)
  rewrite erase-stamp-high v = ⇓ₑ-deref-● (sim sec M⇓a)
sim sec (⇓-deref {v = v} { = high} {high} M⇓a eq)
  rewrite erase-stamp-high v = ⇓ₑ-deref-● (sim sec M⇓a)
sim {pc = pc} sec (⇓-assign? {L = L} {M} {V} { = } {ℓ₁} L⇓a M⇓V pc⋎ℓ≼ℓ₁)
  with  | ℓ₁
...   | low | low =
  ⇓ₑ-assign? (sim sec L⇓a) goal pc⋎ℓ≼ℓ₁
  where
  goal : erase-μᴸ _  _  erase M ⇓ₑ
      erase (stamp-val V (⇓-value M⇓V) low)  erase-μᴸ _
  goal rewrite stamp-val-low (⇓-value M⇓V) = sim (⇓-pres-sec sec L⇓a) M⇓V
...   | low | high =
  ⇓ₑ-assign?-● (sim sec L⇓a) (sim (⇓-pres-sec sec L⇓a) M⇓V)
...   | high | high =
  ⇓ₑ-assign?-● (sim sec L⇓a) (sim (⇓-pres-sec sec L⇓a) M⇓V)
...   | high | low with pc
...   | low  = case pc⋎ℓ≼ℓ₁ of λ where ()
...   | high = case pc⋎ℓ≼ℓ₁ of λ where ()