module CC2.Stamping where

open import Data.Nat
open import Data.List
open import Data.Maybe
open import Data.Product using (_×_; ; ∃-syntax; Σ; Σ-syntax) renaming (_,_ to ⟨_,_⟩)
open import Relation.Binary.PropositionalEquality using (_≡_; _≢_; refl; subst; subst₂; cong; cong₂; sym)
open import Function using (case_of_)

open import Common.Utils
open import Common.Types
open import Common.Coercions
open import Memory.HeapContext
open import CoercionExpr.Stamping
open import CC2.Syntax
open import CC2.Typing
open import CC2.Values


{- Stamping on CC terms -}
stamp-val :  V  Value V  (A : Type)  StaticLabel  Term
stamp-val V (V-raw v)             _ low  = V
stamp-val V (V-raw v) (T of l high) high = V
stamp-val V (V-raw v) (T of l low ) high =
  V  cast (coerceᵣ-id T) (id (l low)  ) 
stamp-val (V  c ) (V-cast v i) A  = V  stamp-ir c i  
stamp-val V v A  =   -- impossible


-- Stamping is well-typed
stamp-val-wt :  {Σ gc ℓv A V }
   (v : Value V)
   (⊢V : [] ; Σ ; gc ; ℓv  V  A)
    ----------------------------------------------------------------
   [] ; Σ ; gc ; ℓv  stamp-val V v A   stamp A (l )
stamp-val-wt {A = A} { = low} (V-raw V-addr) ⊢V rewrite stamp-low A = ⊢V
stamp-val-wt { = high} (V-raw V-addr) (⊢addr { = low} eq) =
  ⊢cast (⊢addr eq)
stamp-val-wt { = high} (V-raw V-addr) (⊢addr { = high} eq) = ⊢addr eq
stamp-val-wt {A = A} { = low} (V-raw V-ƛ) ⊢V rewrite stamp-low A = ⊢V
stamp-val-wt { = high} (V-raw V-ƛ) (⊢lam { = low} ⊢N) =
  ⊢cast (⊢lam ⊢N)
stamp-val-wt { = high} (V-raw V-ƛ) (⊢lam { = high} ⊢N) = ⊢lam ⊢N
stamp-val-wt {A = A} { = low} (V-raw V-const) ⊢V rewrite stamp-low A = ⊢V
stamp-val-wt { = high} (V-raw V-const) (⊢const { = low}) =
  ⊢cast ⊢const
stamp-val-wt { = high} (V-raw V-const) (⊢const { = high}) = ⊢const
stamp-val-wt {A = A} {V} {} (V-cast v i) (⊢cast ⊢V) = ⊢cast ⊢V


-- Stamping a value returns another value
stamp-val-value :  {Σ gc ℓv A V }
   (v : Value V)
   (⊢V : [] ; Σ ; gc ; ℓv  V  A)
   Value (stamp-val V v A )
stamp-val-value { = low} (V-raw V-addr) ⊢V = V-raw V-addr
stamp-val-value { = high} (V-raw V-addr) (⊢addr { = low} _) =
  V-cast V-addr (ir-ref (up id))
stamp-val-value { = high} (V-raw V-addr) (⊢addr { = high} _) = V-raw V-addr
stamp-val-value { = low} (V-raw V-ƛ) ⊢V = V-raw V-ƛ
stamp-val-value { = high} (V-raw V-ƛ) (⊢lam { = low} _) =
  V-cast V-ƛ (ir-fun (up id))
stamp-val-value { = high} (V-raw V-ƛ) (⊢lam { = high} _) = V-raw V-ƛ
stamp-val-value { = low} (V-raw V-const) ⊢V = V-raw V-const
stamp-val-value { = high} (V-raw V-const) (⊢const { = low}) =
  V-cast V-const (ir-base (up id)  ()))
stamp-val-value { = high} (V-raw V-const) (⊢const { = high}) = V-raw V-const
stamp-val-value (V-cast v i) ⊢V = V-cast v (stamp-ir-irreducible i)


stamp-val-low :  {Σ gc ℓv A V}
   (v : Value V)
   (⊢V : [] ; Σ ; gc ; ℓv  V  A)
   stamp-val V v A low  V
stamp-val-low (V-raw V-addr) ⊢V = refl
stamp-val-low (V-raw V-ƛ) ⊢V = refl
stamp-val-low (V-raw V-const) ⊢V = refl
stamp-val-low (V-cast v (ir-base (id {l low}) x₁)) ⊢V = refl
stamp-val-low (V-cast v (ir-base (id {l high}) x₁)) ⊢V = refl
stamp-val-low (V-cast v (ir-base (inj id) x₁)) ⊢V = refl
stamp-val-low (V-cast v (ir-base (inj (up id)) x₁)) ⊢V = refl
stamp-val-low (V-cast v (ir-base (up id) x₁)) ⊢V = refl
stamp-val-low (V-cast v (ir-ref (id {l low}))) ⊢V = refl
stamp-val-low (V-cast v (ir-ref (id {l high}))) ⊢V = refl
stamp-val-low (V-cast v (ir-ref (inj id))) ⊢V = refl
stamp-val-low (V-cast v (ir-ref (inj (up id)))) ⊢V = refl
stamp-val-low (V-cast v (ir-ref (up id))) ⊢V = refl
stamp-val-low (V-cast v (ir-fun (id {l low}))) ⊢V = refl
stamp-val-low (V-cast v (ir-fun (id {l high}))) ⊢V = refl
stamp-val-low (V-cast v (ir-fun (inj id))) ⊢V = refl
stamp-val-low (V-cast v (ir-fun (inj (up id)))) ⊢V = refl
stamp-val-low (V-cast v (ir-fun (up id))) ⊢V = refl