import BidirTT.Value

namespace BidirTT

abbrev CxtEntry := Name × Val

structure Cxt where
  env   : Env
  types : List CxtEntry
  lvl   : Lvl
  envWf : env.length = lvl
  typesWf : types.length = lvl

def Cxt.empty : Cxt := ⟨[], [], 0, rfl, rfl⟩

instance : Inhabited Cxt := ⟨Cxt.empty⟩

def Cxt.bind (cxt : Cxt) (x : Name) (a : Val) : Cxt :=
  { env   := .neu (.var cxt.lvl) :: cxt.env
  , types := (x, a) :: cxt.types
  , lvl   := cxt.lvl + 1
  , envWf := by simp [cxt.envWf]
  , typesWf := by simp [cxt.typesWf] }

def Cxt.define (cxt : Cxt) (x : Name) (v a : Val) : Cxt :=
  { env   := v :: cxt.env
  , types := (x, a) :: cxt.types
  , lvl   := cxt.lvl + 1
  , envWf := by simp [cxt.envWf]
  , typesWf := by simp [cxt.typesWf] }

private def castFin {n m : Nat} (h : n = m) (i : Fin n) : Fin m :=
  ⟨i.val, h ▸ i.isLt⟩

private def lookupGo (x : Name) : (entries : List CxtEntry) → Option (Fin entries.length × Val)
  | [] => none
  | (y, a) :: rest =>
      if x == y then
        some (⟨0, by simp⟩, a)
      else
        match lookupGo x rest with
        | some (i, a) =>
            some (⟨i.val + 1, by simp [i.isLt]⟩, a)
        | none =>
            none

def Cxt.isWellFormed (cxt : Cxt) : Bool :=
  cxt.env.length == cxt.lvl && cxt.types.length == cxt.lvl

def Cxt.typeAt (cxt : Cxt) (idx : Fin cxt.lvl) : CxtEntry :=
  cxt.types.get (castFin cxt.typesWf.symm idx)

def Cxt.valueAt (cxt : Cxt) (idx : Fin cxt.lvl) : Val :=
  cxt.env.get (castFin cxt.envWf.symm idx)

def Cxt.lookup (cxt : Cxt) (x : Name) : Option (Fin cxt.lvl × Val) :=
  match lookupGo x cxt.types with
  | some (idx, a) =>
      some (castFin cxt.typesWf idx, a)
  | none =>
      none

end BidirTT
