///|
// A value owns its textual representation and optional internal representation.
// Reading text does not round-trip a numeric payload through that text. Lists
// hold values, not encodings, and mutations always build a separate container.
struct TclValue {
  text : String
  mut payload : ValuePayload
} derive(Debug)

///|
enum ValuePayload {
  Plain
  Numeric(Number)
  Items(Array[TclValue])
  Pairs(Array[(TclValue, TclValue)])
} derive(Debug)

///|
fn text_value(text : String) -> TclValue {
  { text, payload: Plain, }
}

///|
fn number_value(value : Number) -> TclValue raise TclError {
  { text: value.text(), payload: Numeric(value), }
}

///|
fn list_value(values : Array[TclValue]) -> TclValue raise TclError {
  { text: format_list(values.map(v => v.text)), payload: Items(values.copy()), }
}

///|
fn TclValue::as_list(self : TclValue) -> Array[TclValue] raise TclError {
  match self.payload {
    Items(values) => values.copy()
    Pairs(pairs) => {
      let values = []
      for (key, value) in pairs {
        values.push(key)
        values.push(value)
      }
      self.payload = Items(values)
      values.copy()
    }
    _ => {
      let values = parse_list(self.text).map(text_value)
      self.payload = Items(values)
      values.copy()
    }
  }
}

///|
fn TclValue::as_number(self : TclValue) -> Number? {
  match self.payload {
    Numeric(value) => Some(value)
    _ => {
      let value = number(self.text)
      if value is Some(n) {
        self.payload = Numeric(n)
      }
      value
    }
  }
}

///|
fn TclValue::truth(self : TclValue) -> Bool raise TclError {
  match self.as_number() {
    Some(n) => {
      let value = n.double()
      if value.is_nan() {
        raise Invalid("expected boolean")
      }
      value != 0.0
    }
    None => boolean(self.text)
  }
}

///|
fn TclValue::as_whole(self : TclValue) -> @bigint.BigInt raise TclError {
  match self.payload {
    Numeric(Small(n)) => @bigint.BigInt::from_int(n)
    Numeric(Whole(n)) => n
    Numeric(Real(_)) => raise Invalid("expected integer")
    _ => whole(self.text)
  }
}

///|
fn Interpreter::execute_value_script(
  self : Interpreter,
  value : TclValue,
  depth : Int,
  discard_result? : Bool = false,
) -> TclValue raise TclError {
  if value.text.length() > 100000 {
    raise Invalid("script size limit")
  }
  if depth > 64 {
    raise Invalid("execution nesting limit")
  }
  match value.payload {
    Items(items) => {
      if items.is_empty() {
        return text_value("")
      }
      if items.length() > 4096 {
        raise Invalid("expanded argument limit")
      }
      self.command_value(items, depth, discard_result~) catch {
        error =>
          raise Signal(
            self.annotate_error(
              outcome(error),
              value.text,
              1,
              items.map(v => v.text),
            ),
          )
      }
    }
    _ => self.execute_value(value.text, depth, discard_result~)
  }
}

///|
fn script_arguments(values : Array[TclValue]) -> TclValue raise TclError {
  if values.length() == 1 {
    values[0]
  } else {
    concat_values(values)
  }
}