{-# OPTIONS --safe #-}
{-# OPTIONS --with-K #-}
module Pat.Reduction where

open import Data.Maybe.Base using (just; nothing)
open import Data.Nat.Base using (; suc; zero; _≥‴_; ≤‴-refl; ≤‴-step)
open import Function.Base using (id)
open import Relation.Binary.PropositionalEquality.Core using (_≡_; refl; sym)

open import Pat.Context
open import Pat.Term
open import Pat.Matching
open import Pat.Rewriting
open import Pat.Substitution

variable
  e e₁ e₂ e₃ e′ e₁′ e₂′ e₃′ : Term n Γ A
  p : Pattern n Γ Δ A Π
  σ : Subst Γ Γ′

_[_] : Term n (Γ ,[ Δ  A ^ m≥n ]) B  Term m (Γ ↾≥ m≥n ++ Δ) A  Term n Γ B
e₁ [ e₂ ] = subst (lift id , term e₂) e₁

_[_]ˢ : Term n (Γ ++ Π) B  Subst Π Γ  Term n Γ B
e [ σ  = subst (lift id ++ˢ σ) e

data Value : Term n Γ A  Set where
  `λ⟨_⟩_ :  Δ  (e : Term n (Γ ,[ inject₁ Δ  A ^ ≤‴-refl ]) B)  Value (`λ⟨ Δ  e)
  `true : Value {Γ = Γ} `true
  `false : Value {Γ = Γ} `false
  ⟨_⟩ : (e : Term (suc n) (Γ ) A)  Value {Γ = Γ}  e 
  `wrap :  Δ  (e : Term n (Γ ++ inject₁ Δ) A)  (v : Value e)  Value (`wrap Δ e)

infix 4 _-→_
data _-→_ {n} {Γ : Context n} : Term n Γ A  Term n Γ A  Set where
  β-λ⟨⟩· : Value e₂  (`λ⟨ Δ  e₁) · e₂ -→ e₁ [ e₂ ]
  β-rewritȩ⟨⟩⟨⟩ : (`rewritȩ {Π = Π} p  e₁   e₂ ) -→  rewritȩ p (transportContext (++-inject₁-↾ Π) e₁) e₂  
  β-if-true : `if `true `then e₁ `else e₂ -→ e₁
  β-if-false : `if `false `then e₁ `else e₂ -→ e₂
  β-let⟨⟩ : {e₂ : Term n (Γ ,[ Δ  A ^ ≤‴-step ≤‴-refl ]) B}
     `let⟨ Δ  ( e₁ ) e₂ -→ e₂ [ transportContext (++-inject₁-↾ Δ) e₁ ]
  β-letwrap : (Value (`wrap Δ e₁))  `letwrap Δ (`wrap Δ e₁) e₂ -→ e₂ [ e₁ ]
  β-iflet⟨⟩₁ : {e₂ : Term n (Γ ++ inject₁ Π) B}
     match p (transportContext (++-inject₁-↾ Δ) e₁)  just σ
     `iflet⟨ Δ  p  e₁  e₂ e₃ -→ e₂ [ ⊩-↾⁻ σ 
  β-iflet⟨⟩₂ : {e₂ : Term n (Γ ++ inject₁ Π) B}
     match p (transportContext (++-inject₁-↾ Δ) e₁)  nothing
     `iflet⟨ Δ  p  e₁  e₂ e₃ -→ e₃

  ξ-∙₁ : e₁ -→ e₁′  e₁ · e₂ -→ e₁′ · e₂
  ξ-∙₂ : Value e₁  e₂ -→ e₂′  e₁ · e₂ -→ e₁ · e₂′
  ξ-if₁ : e₁ -→ e₁′  `if e₁ `then e₂ `else e₃ -→ `if e₁′ `then e₂ `else e₃
  ξ-let⟨⟩₁ : e₁ -→ e₁′  `let⟨ Δ  e₁ e₂ -→ `let⟨ Δ  e₁′ e₂
  ξ-wrap : e -→ e′  `wrap Δ e -→ `wrap Δ e′
  ξ-letwrap₁ : e₁ -→ e₁′  `letwrap Δ e₁ e₂ -→ `letwrap Δ e₁′ e₂
  ξ-iflet⟨⟩₁ : e₁ -→ e₁′  `iflet⟨ Δ  p e₁ e₂ e₃ -→ `iflet⟨ Δ  p e₁′ e₂ e₃
  ξ-rewritȩ₁ : e₁ -→ e₁′  `rewritȩ {B = B} p e₁ e₂ -→ `rewritȩ p e₁′ e₂
  ξ-rewritȩ₂ : Value e₁  e₂ -→ e₂′  `rewritȩ {B = B} p e₁ e₂ -→ `rewritȩ p e₁ e₂′

data Progress : Term n Γ A  Set where
  p-step : e -→ e′  Progress e
  p-value : Value e  Progress e

progress : (e : Term n (inject₁ Γ) A)  Progress e


progress' : inject₁ Γ′  Γ  (e : Term n Γ A)  Progress e
progress' refl = progress

progress (` x `with σ) with ()inject₁-∌-refl x
progress `true = p-value `true
progress `false = p-value `false
progress (`if e₁ `then e₂ `else e₃) with progress e₁
... | p-step e₁-→e₁′ = p-step (ξ-if₁ e₁-→e₁′)
... | p-value `true = p-step β-if-true
... | p-value `false = p-step β-if-false
progress (`λ⟨ Δ  e) = p-value (`λ⟨ Δ  e)
progress (e₁ · e₂) with progress e₁
... | p-step e₁-→e₁′ = p-step (ξ-∙₁ e₁-→e₁′)
... | p-value ve₁@(`λ⟨ Δ  e₁′) with progress' (inject₁-++ Δ) e₂
... | p-step e₂-→e₂′ = p-step (ξ-∙₂ ve₁ e₂-→e₂′)
... | p-value ve₂ = p-step (β-λ⟨⟩· ve₂)
progress  e  = p-value  e 
progress (`let⟨ Δ  e₁ e₂) with progress' (inject₁-++ Δ) e₁
... | p-step e₁-→e₁′ = p-step (ξ-let⟨⟩₁ e₁-→e₁′)
... | p-value  e  = p-step β-let⟨⟩
progress (`wrap Δ e) with progress' (inject₁-++ Δ) e
... | p-step e-→e′ = p-step (ξ-wrap e-→e′)
... | p-value ve = p-value (`wrap Δ e ve)
progress (`letwrap Δ e₁ e₂) with progress e₁
... | p-step e₁-→e₁′ = p-step (ξ-letwrap₁ e₁-→e₁′)
... | p-value v₁@(`wrap _ e ve) = p-step (β-letwrap v₁)
progress (`iflet⟨ Δ  p e₁ e₂ e₃) with progress' (inject₁-++ Δ) e₁
... | p-step e₁-→e₁′ = p-step (ξ-iflet⟨⟩₁ e₁-→e₁′)
... | p-value  e  with match p (transportContext (++-inject₁-↾ Δ) e) in eq
... | just σ = p-step (β-iflet⟨⟩₁ eq)
... | nothing = p-step (β-iflet⟨⟩₂ eq)
progress (`rewritȩ {Π = Π} p e₁ e₂) with progress' (inject₁-++ Π) e₁
... | p-step e₁-→e₁′ = p-step (ξ-rewritȩ₁ e₁-→e₁′)
... | p-value ve₁@( e₁ ) with progress e₂
... | p-step e₂-→e₂′ = p-step (ξ-rewritȩ₂  e₁  e₂-→e₂′)
... | p-value  e₂  = p-step β-rewritȩ⟨⟩⟨⟩