module CC.ApplyCastWT where

open import Data.List hiding ([_])
open import Data.Product using (_×_; ∃-syntax; proj₁; proj₂) renaming (_,_ to ⟨_,_⟩)
open import Relation.Nullary using (¬_; Dec; yes; no)
open import Relation.Nullary.Negation using (contradiction)
open import Relation.Binary.PropositionalEquality using (_≡_; refl)
open import Function using (case_of_)

open import Common.Utils
open import Common.Types
open import CC.CCStatics
open import CC.ApplyCast


applycast-progress :  {Σ A B V} {c : Cast A  B}
   [] ; Σ ; l low ; low  V  A
   Value V  Active c
    ----------------------------------
   ∃[ M ] ApplyCast V , c  M
applycast-progress ⊢V v (A-base-id c) =  _ , cast-base-id 
applycast-progress ⊢V v (A-base-proj (cast (` ι of ) (` ι of l ) p _)) =
  case  v , canonical⋆ ⊢V v  of λ where
   V-cast w _ , _ , _ , cast (` ι of l ℓ′) (` ι of ) q (~-ty ~⋆ ~-ι) ,
    W , refl , I-base-inj _ , ⊢W , <:-ty <:-⋆ <:-ι  
      case ℓ′ ≼?  of λ where
      (yes ℓ′≼ℓ) 
         _ , cast-base-proj ℓ′≼ℓ 
      (no  ℓ′⋠ℓ) 
         _ , cast-base-proj-blame ℓ′⋠ℓ 
applycast-progress ⊢V v (A-fun (cast ( _  C₁  D₁ of ) ( _  C₂  D₂ of g) p (~-ty _ d~)) a) =
  case  v , canonical⋆ ⊢V v  of λ where
   V-cast w _ , _ , _ , cast ( gc₁′  A₁  A₂ of l ℓ′) ( gc₂′  B₁  B₂ of ) q (~-ty ~⋆ A~B) ,
    W , refl , I-fun _ I-label I-label , ⊢W , <:-ty <:-⋆ B<:C  
      case a of λ where
      A-id⋆ 
        let c~′ = ~-ty l~ A~B in
        let d~′ = ~-ty ~⋆ d~  in
         _ , cast-fun-id⋆ {c~′ = c~′} {d~′} 
      (A-proj {}) 
        case ℓ′ ≼?  of λ where
        (yes ℓ′≼ℓ) 
          let c~′ = ~-ty l~ A~B in
          let d~′ = ~-ty l~ d~  in
           _ , cast-fun-proj {c~′ = c~′} {d~′} ℓ′≼ℓ 
        (no  ℓ′⋠ℓ) 
           _ , cast-fun-proj-blame ℓ′⋠ℓ 
applycast-progress ⊢V v (A-fun-pc (cast (   C₁  C₂ of l ℓ₁) ( gc  D₁  D₂ of g₂) p (~-ty ℓ₁~g₂ (~-fun _ C₁~C₂ D₁~D₂))) a I-label) =
  case  v , canonical-pc⋆ ⊢V v  of λ where
   V-cast w _ , _ , _ , cast ( l pc′  A₁  A₂ of g₁′) (   B₁  B₂ of g₂′) q (~-ty g₁′~g₂′ (~-fun _ A₁~B₁ A₂~B₂)) ,
    W , refl , I-fun _ I-label I-label , ⊢W , <:-ty g₂′<:g₁ (<:-fun <:-⋆ C₁<:B₁ B₂<:C₂)  
      case a of λ where
      A-id⋆ 
        let c~′ = ~-ty g₁′~g₂′ (~-fun l~ A₁~B₁ A₂~B₂) in
        let d~′ = ~-ty ℓ₁~g₂   (~-fun ~⋆ C₁~C₂ D₁~D₂) in
         _ , cast-fun-pc-id⋆ {c~′ = c~′} {d~′} 
      (A-proj {pc}) 
        case pc ≼? pc′ of λ where
        (yes pc≼pc′) 
          let c~′ = ~-ty g₁′~g₂′ (~-fun l~ A₁~B₁ A₂~B₂) in
          let d~′ = ~-ty ℓ₁~g₂   (~-fun l~ C₁~C₂ D₁~D₂) in
           _ , cast-fun-pc-proj {c~′ = c~′} {d~′} pc≼pc′ 
        (no  pc⋠pc′)   _ , cast-fun-pc-proj-blame pc⋠pc′ 
applycast-progress ⊢V v (A-ref (cast (Ref C of ) (Ref D of g) p (~-ty _ RefC~RefD)) a) =
  case  v , canonical⋆ ⊢V v  of λ where
   V-cast w _ , _ , _ , cast (Ref A of l ℓ′) (Ref B of ) q (~-ty ~⋆ RefA~RefB) ,
    W , refl , I-ref _ I-label I-label , ⊢W , <:-ty <:-⋆ RefB<:RefC  
      case a of λ where
      A-id⋆ 
        let c~′ = ~-ty l~ RefA~RefB in
        let d~′ = ~-ty ~⋆ RefC~RefD in
         _ , cast-ref-id⋆ {c~′ = c~′} {d~′} 
      (A-proj {}) 
        case ℓ′ ≼?  of λ where
        (yes ℓ′≼ℓ) 
          let c~′ = ~-ty l~ RefA~RefB in
          let d~′ = ~-ty l~ RefC~RefD in
           _ , cast-ref-proj {c~′ = c~′} {d~′} ℓ′≼ℓ 
        (no  ℓ′⋠ℓ)   _ , cast-ref-proj-blame ℓ′⋠ℓ 
applycast-progress ⊢V v (A-ref-ref (cast (Ref (S of ) of l ℓ₁) (Ref (T of g₂₁) of g₂) p (~-ty ℓ₁~g₂ (~-ref (~-ty _ S~T)))) a I-label) =
  case  v , canonical-ref⋆ ⊢V v  of λ where
   V-cast w _ , _ , _ , cast (Ref (S′ of l ℓ₁′) of g₁′) (Ref (T′ of ) of g₂′) q (~-ty g₁′~g₂′ (~-ref (~-ty _ S′~T′))) ,
    W , refl , I-ref _ I-label I-label , ⊢W , <:-ty g₂′<:g₁ (<:-ref (<:-ty <:-⋆ T′<:S) (<:-ty <:-⋆ S<:T′))  
      case a of λ where
      A-id⋆ 
        let c~′ = ~-ty g₁′~g₂′ (~-ref (~-ty l~ S′~T′)) in
        let d~′ = ~-ty ℓ₁~g₂   (~-ref (~-ty ~⋆ S~T)) in
         _ , cast-ref-ref-id⋆ {c~′ = c~′} {d~′} 
      (A-proj {ℓ₁}) 
        case ℓ₁′ =? ℓ₁ of λ where
        (yes refl) 
          let c~′ = ~-ty g₁′~g₂′ (~-ref (~-ty l~ S′~T′)) in
          let d~′ = ~-ty ℓ₁~g₂   (~-ref (~-ty l~ S~T)) in
           _ , cast-ref-ref-proj {c~′ = c~′} {d~′} refl 
        (no ℓ₁′≢ℓ₁) 
           _ , cast-ref-ref-proj-blame ℓ₁′≢ℓ₁ 


applycast-pres :  {Σ gc pc A B V M} {c : Cast A  B}
   [] ; Σ ; l low ; low  V  A
   Value V  Active c
   ApplyCast V , c  M
    ----------------------------------
   [] ; Σ ; gc ; pc  M  B
applycast-pres ⊢V v (A-base-id c) cast-base-id = ⊢value-pc ⊢V v
applycast-pres ⊢V v (A-base-proj _) (cast-base-proj ℓ₁≼ℓ₂) =
  case  v , canonical⋆ ⊢V v  of λ where
   V-cast w _ , _ , _ , cast (` ι of l ℓ₁) (` ι of ) q (~-ty ~⋆ ~-ι) ,
    W , refl , I-base-inj _ , ⊢W , <:-ty <:-⋆ <:-ι  
      ⊢sub (⊢value-pc ⊢W w) (<:-ty (<:-l ℓ₁≼ℓ₂) <:-ι)
applycast-pres ⊢V v (A-base-proj _) (cast-base-proj-blame _) = ⊢err
applycast-pres ⊢V v (A-fun (cast ( _  C₁  D₁ of ) ( _  C₂  D₂ of ) p _) A-id⋆) cast-fun-id⋆ =
  case  v , canonical⋆ ⊢V v  of λ where
   V-cast w _ , _ , _ , cast ( gc₁′  A₁  A₂ of l ℓ′) ( gc₂′  B₁  B₂ of ) q (~-ty ~⋆ A~B) ,
    W , refl , I-fun _ I-label I-label , ⊢W , <:-ty <:-⋆ B<:C  
      ⊢cast (⊢sub (⊢cast (⊢value-pc ⊢W w)) (<:-ty <:ₗ-refl B<:C))
applycast-pres ⊢V v (A-fun (cast ( _  C₁  D₁ of ) ( _  C₂  D₂ of l _) p _) A-proj) (cast-fun-proj ℓ′≼ℓ) =
  case  v , canonical⋆ ⊢V v  of λ where
   V-cast w _ , _ , _ , cast ( gc₁′  A₁  A₂ of l ℓ′) ( gc₂′  B₁  B₂ of ) q (~-ty ~⋆ A~B) ,
    W , refl , I-fun _ I-label I-label , ⊢W , <:-ty <:-⋆ B<:C  
      ⊢cast (⊢sub (⊢cast (⊢sub (⊢value-pc ⊢W w) (<:-ty (<:-l ℓ′≼ℓ) <:ᵣ-refl))) (<:-ty <:ₗ-refl B<:C))
applycast-pres ⊢V v a (cast-fun-proj-blame _) = ⊢err
applycast-pres ⊢V v (A-fun-pc (cast (   C₁  C₂ of l ℓ₁) (   D₁  D₂ of g₂) p _) A-id⋆ I-label) cast-fun-pc-id⋆ =
  case  v , canonical-pc⋆ ⊢V v  of λ where
   V-cast w _ , _ , _ , cast ( l pc′  A₁  A₂ of g₁′) (   B₁  B₂ of g₂′) q (~-ty g₁′~g₂′ (~-fun _ A₁~B₁ A₂~B₂)) ,
    W , refl , I-fun _ I-label I-label , ⊢W , <:-ty g₂′<:g₁ (<:-fun <:-⋆ C₁<:B₁ B₂<:C₂)  
      ⊢cast (⊢sub (⊢cast (⊢value-pc ⊢W w)) (<:-ty g₂′<:g₁ (<:-fun <:ₗ-refl C₁<:B₁ B₂<:C₂)))
applycast-pres ⊢V v (A-fun-pc (cast (   C₁  C₂ of g₁) ( l _  D₁  D₂ of g₂) p _) A-proj I-label) (cast-fun-pc-proj pc≼pc′) =
  case  v , canonical-pc⋆ ⊢V v  of λ where
   V-cast w _ , _ , _ , cast ( l pc′  A₁  A₂ of g₁′) (   B₁  B₂ of g₂′) q (~-ty g₁′~g₂′ (~-fun _ A₁~B₁ A₂~B₂)) ,
    W , refl , I-fun _ I-label I-label , ⊢W , <:-ty g₂′<:g₁ (<:-fun <:-⋆ C₁<:B₁ B₂<:C₂)  
      ⊢cast (⊢sub (⊢cast (⊢sub (⊢value-pc ⊢W w) (<:-ty <:ₗ-refl (<:-fun (<:-l pc≼pc′) <:-refl <:-refl))))
                  (<:-ty g₂′<:g₁ (<:-fun <:ₗ-refl C₁<:B₁ B₂<:C₂)))
applycast-pres ⊢V v a (cast-fun-pc-proj-blame _) = ⊢err
applycast-pres ⊢V v (A-ref (cast (Ref C of ) (Ref B of ) p _) A-id⋆) cast-ref-id⋆ =
  case  v , canonical⋆ ⊢V v  of λ where
   V-cast w _ , _ , _ , cast (Ref A of l ℓ′) (Ref B of ) q (~-ty ~⋆ RefA~RefB) ,
    W , refl , I-ref _ I-label I-label , ⊢W , <:-ty <:-⋆ RefB<:RefC  
      ⊢cast (⊢sub (⊢cast (⊢value-pc ⊢W w)) (<:-ty <:ₗ-refl RefB<:RefC))
applycast-pres ⊢V v (A-ref (cast (Ref C of ) (Ref D of l _) p _) A-proj) (cast-ref-proj ℓ′≼ℓ) =
  case  v , canonical⋆ ⊢V v  of λ where
   V-cast w _ , _ , _ , cast (Ref A of l ℓ′) (Ref B of ) q (~-ty ~⋆ RefA~RefB) ,
    W , refl , I-ref _ I-label I-label , ⊢W , <:-ty <:-⋆ RefB<:RefC  
      ⊢cast (⊢sub (⊢cast (⊢sub (⊢value-pc ⊢W w) (<:-ty (<:-l ℓ′≼ℓ) <:ᵣ-refl))) (<:-ty <:ₗ-refl RefB<:RefC))
applycast-pres ⊢V v a (cast-ref-proj-blame _) = ⊢err
applycast-pres ⊢V v (A-ref-ref (cast (Ref (S of ) of l _) (Ref (T of ) of _) p _) A-id⋆ I-label) cast-ref-ref-id⋆ =
  case  v , canonical-ref⋆ ⊢V v  of λ where
   V-cast w _ , _ , _ , cast (Ref (S′ of l ℓ₁′) of g₁′) (Ref (T′ of ) of g₂′) q (~-ty g₁′~g₂′ (~-ref (~-ty _ S′~T′))) ,
    W , refl , I-ref _ I-label I-label , ⊢W , <:-ty g₂′<:g₁ (<:-ref (<:-ty <:-⋆ T′<:S) (<:-ty <:-⋆ S<:T′))  
      ⊢cast (⊢sub (⊢cast (⊢value-pc ⊢W w)) (<:-ty g₂′<:g₁ (<:-ref (<:-ty <:ₗ-refl T′<:S) (<:-ty <:ₗ-refl S<:T′))))
applycast-pres ⊢V v (A-ref-ref (cast (Ref (S of ) of l _) (Ref (T of l _) of _) p _) A-proj I-label) (cast-ref-ref-proj refl) =
  case  v , canonical-ref⋆ ⊢V v  of λ where
   V-cast w _ , _ , _ , cast (Ref (S′ of l ℓ₁′) of g₁′) (Ref (T′ of ) of g₂′) q (~-ty g₁′~g₂′ (~-ref (~-ty _ S′~T′))) ,
    W , refl , I-ref _ I-label I-label , ⊢W , <:-ty g₂′<:g₁ (<:-ref (<:-ty <:-⋆ T′<:S) (<:-ty <:-⋆ S<:T′))  
      ⊢cast (⊢sub (⊢cast (⊢value-pc ⊢W w)) (<:-ty g₂′<:g₁ (<:-ref (<:-ty <:ₗ-refl T′<:S) (<:-ty <:ₗ-refl S<:T′))))
applycast-pres ⊢V v a (cast-ref-ref-proj-blame _) = ⊢err