module Compile.Compile where

open import Data.Nat
open import Data.List
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.Binary.PropositionalEquality using (_≡_; refl; cong; subst; sym)
open import Function using (case_of_)

open import Syntax

open import Common.Utils
open import Common.BlameLabels
open import Common.Types
open import Common.TypeBasedCast
open import Surface2.Lang     renaming (`_ to `ᴳ_;
                                        $_of_ to $ᴳ_of_)
open import CC2.Syntax public renaming (Term to CCTerm)
open import CC2.Typing public


compile :  {Γ gc A} (M : Term)  Γ ; gc ⊢ᴳ M  A  CCTerm
compile ($ᴳ k of ) ⊢const = $ k
compile (`ᴳ x) (⊢var x∈Γ)  = ` x
compile (ƛ g , A ˙ N of ) (⊢lam ⊢N) = ƛ (compile N ⊢N)
compile (L · M at p) (⊢app {gc = gc} {gc′} {A = A} {A′} {B} {g = g} ⊢L ⊢M A′≲A g≾gc′ gc≾gc′) =
  case all-specific? [ gc , g , gc′ ] of λ where
  (yes (as-cons ( ℓ₁)  (as-cons ( ℓ₂)  (as-cons ( ℓ₃) as-nil)))) 
    case  g≾gc′ , gc≾gc′  of λ where
     ≾-l ℓ≼ℓᶜ , ≾-l pc≼ℓᶜ  
      let sub :  l ℓ₃  A  B of l ℓ₂ <:  l (ℓ₁  ℓ₂)  A  B of l ℓ₂
          sub = <:-ty <:ₗ-refl (<:-fun (<:-l (ℓ₁⋎ℓ₂≼ℓ pc≼ℓᶜ ℓ≼ℓᶜ)) <:-refl <:-refl) in
      app (compile L ⊢L  coerce (<:→≲ sub) p ) (compile M ⊢M  coerce A′≲A p ) A B ℓ₂
  (no _) 
    case B of λ where
    (T of g′) 
      let csub₁ :  gc′  A  (T of g′) of g     A  (T of ) of 
          csub₁ = ≲-ty ≾-⋆r (≲-fun ≾-⋆l ≲-refl (≲-ty ≾-⋆r ≲ᵣ-refl)) in
      let csub₂ : T of   stamp (T of g′) g
          csub₂ = ≲-ty ≾-⋆l ≲ᵣ-refl in
      (app⋆ (compile L ⊢L  coerce csub₁ p ) (compile M ⊢M  coerce A′≲A p ) A T)  coerce csub₂ p 
compile (if L then M else N at p) (⊢if {gc = gc} {A = A} {B} {C} {g = g} ⊢L ⊢M ⊢N A∨̃B≡C) =
  case consis-join-≲-inv {A} {B} A∨̃B≡C of λ where
   A≲C , B≲C  
      let L′ = compile L ⊢L in
      let M′ = compile M ⊢M  coerce A≲C p  in
      let N′ = compile N ⊢N  coerce B≲C p  in
      case all-specific? [ gc , g ] of λ where
      (yes (as-cons ( _) (as-cons ( ) as-nil))) 
        if L′ C  M′ N′
      (no _) 
        case C of λ where
        (T of g′) 
          let csub : stamp C   stamp C g
              csub = proj₁ (~→≲ (stamp-~ ~-refl ⋆~)) in
          let  A≲C , B≲C  = consis-join-≲-inv {A} {B} {C} A∨̃B≡C in
          (if⋆ (L′  inject (` Bool) g ) T (M′  inject T g′ ) (N′  inject T g′ ))  coerce csub p 
compile (M  A at p) (⊢ann {A′ = A′} ⊢M A′≲A) = compile M ⊢M  coerce A′≲A p 
compile (`let M `in N) (⊢let {A = A} ⊢M ⊢N) = `let (compile M ⊢M) A (compile N ⊢N)
compile (ref⟦   M at p) (⊢ref {gc = gc} ⊢M Tg≲Tℓ gc≾ℓ) =
  let M′ = compile M ⊢M  coerce Tg≲Tℓ p  in
  case gc of λ where
  (l ℓᶜ)   ref⟦   M′
         ref?⟦   M′ p
compile (! M at p) (⊢deref {A = A} {g} ⊢M) =
  case g of λ where
  (l )  !  (compile M ⊢M) A 
       
    case A of λ where
    (T of g′) 
      let csub : Ref (T of g′) of g  Ref (T of ) of 
          csub = ≲-ty ≾-⋆r (≲-ref (≲-ty ≾-⋆r ≲ᵣ-refl) (≲-ty ≾-⋆l ≲ᵣ-refl)) in
      !⋆ (compile M ⊢M  coerce csub p ) T
compile (L := M at p) (⊢assign {gc = gc} {A = A} {T} {g} {} ⊢L ⊢M A≲Tĝ g≾ĝ gc≾ĝ) =
  case all-specific? [ gc , g ,  ] of λ where
  (yes (as-cons ( _)  (as-cons ( )  (as-cons ( ℓ̂) as-nil)))) 
      assign (compile L ⊢L) (compile M ⊢M  coerce A≲Tĝ p ) T ℓ̂ 
  (no _) 
    assign? (compile L ⊢L  inject (Ref (T of )) g ) (compile M ⊢M  coerce A≲Tĝ p ) T  p