///|
/// Runtime values available before object and class support is introduced.
pub(all) enum CoreClass {
  ObjectClass
  BoolClass
  ClassClass
  FiberClass
  FnClass
  ListClass
  MapClass
  MapEntryClass
  MapKeySequenceClass
  MapValueSequenceClass
  NullClass
  NumClass
  RangeClass
  SequenceClass
  StringClass
  StringByteSequenceClass
  StringCodePointSequenceClass
} derive(Eq, Debug)

///|
pub extend CoreClass with Eq::{equal, not_equal}

///|
pub extend CoreClass with Debug::{to_repr}

///|
pub fn CoreClass::name(self : CoreClass) -> String {
  match self {
    ObjectClass => "Object"
    BoolClass => "Bool"
    ClassClass => "Class"
    FiberClass => "Fiber"
    FnClass => "Fn"
    ListClass => "List"
    MapClass => "Map"
    MapEntryClass => "MapEntry"
    MapKeySequenceClass => "MapKeySequence"
    MapValueSequenceClass => "MapValueSequence"
    NullClass => "Null"
    NumClass => "Num"
    RangeClass => "Range"
    SequenceClass => "Sequence"
    StringClass => "String"
    StringByteSequenceClass => "StringByteSequence"
    StringCodePointSequenceClass => "StringCodePointSequence"
  }
}

///|
pub(all) enum Value {
  NullValue
  BoolValue(Bool)
  NumberValue(Double)
  StringValue(String)
  RawStringValue(Array[Int])
  RangeValue(Double, Double, Bool)
  ListValue(Int)
  MapValue(Int)
  SequenceValue(Int)
  FunctionValue(Int)
  FiberValue(Int)
  ClassValue(Int)
  MetaClassValue(String)
  ClassAttributesValue(Int, Int)
  InstanceValue(Int)
  SuperValue(Int, Int, String, Bool)
  SuperClassValue(Int, Int)
  CoreClassValue(CoreClass)
  IteratorValue(Int)
  MapEntryValue(Int)
  SystemValue
} derive(Eq, Debug)

///|
/// Result of converting a dynamically typed Wren value at the host boundary.
pub(all) enum ValueConversionResult[T] {
  ValueConverted(T)
  ValueConversionFailed(String, String)
}

///|
pub extend Value with Eq::{equal, not_equal}

///|
pub extend Value with Debug::{to_repr}

///|
pub fn Value::null() -> Value {
  NullValue
}

///|
pub fn Value::bool(value : Bool) -> Value {
  BoolValue(value)
}

///|
pub fn Value::number(value : Double) -> Value {
  NumberValue(value)
}

///|
pub fn Value::string(value : String) -> Value {
  StringValue(value)
}

///|
/// Construct a String from bytes that are not required to be valid UTF-8.
/// This is used by Wren's byte-oriented String primitives.
fn Value::raw_string(bytes : Array[Int]) -> Value {
  RawStringValue(bytes)
}

///|
pub fn Value::range(from : Double, to : Double, inclusive : Bool) -> Value {
  RangeValue(from, to, inclusive)
}

///|
pub fn Value::as_bool(self : Value) -> ValueConversionResult[Bool] {
  match self {
    BoolValue(value) => ValueConverted(value)
    _ => ValueConversionFailed("Bool", self.type_name())
  }
}

///|
pub fn Value::as_number(self : Value) -> ValueConversionResult[Double] {
  match self {
    NumberValue(value) => ValueConverted(value)
    _ => ValueConversionFailed("Num", self.type_name())
  }
}

///|
pub fn Value::as_string(self : Value) -> ValueConversionResult[String] {
  match self {
    StringValue(value) => ValueConverted(value)
    RawStringValue(_) => ValueConversionFailed("valid UTF-8 String", "String")
    _ => ValueConversionFailed("String", self.type_name())
  }
}

///|
pub fn Value::as_range(
  self : Value,
) -> ValueConversionResult[(Double, Double, Bool)] {
  match self {
    RangeValue(from, to, inclusive) => ValueConverted((from, to, inclusive))
    _ => ValueConversionFailed("Range", self.type_name())
  }
}

///|
fn Value::list(identifier : Int) -> Value {
  ListValue(identifier)
}

///|
fn Value::map(identifier : Int) -> Value {
  MapValue(identifier)
}

///|
fn Value::sequence(identifier : Int) -> Value {
  SequenceValue(identifier)
}

///|
fn Value::function(identifier : Int) -> Value {
  FunctionValue(identifier)
}

///|
fn Value::fiber(identifier : Int) -> Value {
  FiberValue(identifier)
}

///|
fn Value::class_value(identifier : Int) -> Value {
  ClassValue(identifier)
}

///|
fn Value::meta_class(name : String) -> Value {
  MetaClassValue(name)
}

///|
fn Value::instance(identifier : Int) -> Value {
  InstanceValue(identifier)
}

///|
fn Value::system() -> Value {
  SystemValue
}

///|
fn Value::core_class(value : CoreClass) -> Value {
  CoreClassValue(value)
}

///|
fn Value::iterator(identifier : Int) -> Value {
  IteratorValue(identifier)
}

///|
fn Value::map_entry(identifier : Int) -> Value {
  MapEntryValue(identifier)
}

///|
/// Wren considers `false` and `null` falsey; every other value is truthy.
pub fn Value::is_truthy(self : Value) -> Bool {
  match self {
    NullValue | BoolValue(false) => false
    _ => true
  }
}

///|
/// Wren 0.4 formats numbers with 14 significant decimal digits. MoonBit's
/// default formatter intentionally preserves more precision, so normalize its
/// shortest round-trippable spelling to Wren's public representation.
fn wren_number_to_text(number : Double) -> String {
  if number.is_nan() {
    return "nan"
  }
  if number.is_pos_inf() {
    return "infinity"
  }
  if number.is_neg_inf() {
    return "-infinity"
  }
  let raw = number.to_string().to_array()
  let mut index = 0
  let mut negative = false
  if raw.length() > 0 && raw[0].to_int() == 45 {
    negative = true
    index = 1
  }
  let digits : Array[Int] = []
  let mut decimal_position = -1
  let mut exponent_sign = 1
  let mut exponent_value = 0
  let mut reading_exponent = false
  while index < raw.length() {
    let code = raw[index].to_int()
    if code == 46 {
      decimal_position = digits.length()
    } else if code == 69 || code == 101 {
      reading_exponent = true
      if decimal_position < 0 {
        decimal_position = digits.length()
      }
    } else if reading_exponent && code == 45 {
      exponent_sign = -1
    } else if reading_exponent && code == 43 {
      exponent_sign = 1
    } else if code >= 48 && code <= 57 {
      if reading_exponent {
        exponent_value = exponent_value * 10 + code - 48
      } else {
        digits.push(code - 48)
      }
    }
    index = index + 1
  }
  if decimal_position < 0 {
    decimal_position = digits.length()
  }
  decimal_position = decimal_position + exponent_sign * exponent_value
  let mut leading = 0
  while leading < digits.length() && digits[leading] == 0 {
    leading = leading + 1
  }
  if leading == digits.length() {
    return if negative ||
      number.reinterpret_as_uint64() == 0x8000_0000_0000_0000UL {
      "-0"
    } else {
      "0"
    }
  }
  decimal_position = decimal_position - leading
  let significant : Array[Int] = []
  for digit_index = leading
      digit_index < digits.length() && significant.length() < 14
      digit_index = digit_index + 1 {
    significant.push(digits[digit_index])
  }
  let mut scientific_exponent = decimal_position - 1
  if leading + significant.length() < digits.length() &&
    digits[leading + significant.length()] >= 5 {
    let mut carry_index = significant.length() - 1
    let mut carry = true
    while carry && carry_index >= 0 {
      if significant[carry_index] == 9 {
        significant[carry_index] = 0
        carry_index = carry_index - 1
      } else {
        significant[carry_index] = significant[carry_index] + 1
        carry = false
      }
    }
    if carry {
      significant[0] = 1
      for reset_index = 1
          reset_index < significant.length()
          reset_index = reset_index + 1 {
        significant[reset_index] = 0
      }
      scientific_exponent = scientific_exponent + 1
    }
  }
  while significant.length() > 1 && significant[significant.length() - 1] == 0 {
    ignore(significant.pop())
  }
  let mut text = if negative { "-" } else { "" }
  let digit_text = fn(digit : Int) -> String {
    (digit + 48).unsafe_to_char().to_string()
  }
  if scientific_exponent < -4 || scientific_exponent >= 14 {
    text = text + digit_text(significant[0])
    if significant.length() > 1 {
      text = text + "."
      for digit_index = 1
          digit_index < significant.length()
          digit_index = digit_index + 1 {
        text = text + digit_text(significant[digit_index])
      }
    }
    let exponent_sign_text = if scientific_exponent >= 0 { "+" } else { "-" }
    text = text + "e" + exponent_sign_text
    let magnitude = if scientific_exponent >= 0 {
      scientific_exponent
    } else {
      -scientific_exponent
    }
    return text + magnitude.to_string()
  }
  let fixed_position = scientific_exponent + 1
  if fixed_position <= 0 {
    text = text + "0."
    for zero_index = fixed_position; zero_index < 0; zero_index = zero_index + 1 {
      text = text + "0"
    }
    for digit in significant {
      text = text + digit_text(digit)
    }
    return text
  }
  for digit_index = 0
      digit_index < fixed_position
      digit_index = digit_index + 1 {
    if digit_index < significant.length() {
      text = text + digit_text(significant[digit_index])
    } else {
      text = text + "0"
    }
  }
  if fixed_position < significant.length() {
    text = text + "."
    for digit_index = fixed_position
        digit_index < significant.length()
        digit_index = digit_index + 1 {
      text = text + digit_text(significant[digit_index])
    }
  }
  text
}

///|
pub fn Value::type_name(self : Value) -> String {
  match self {
    NullValue => "Null"
    BoolValue(_) => "Bool"
    NumberValue(_) => "Num"
    StringValue(_) | RawStringValue(_) => "String"
    RangeValue(_, _, _) => "Range"
    ListValue(_) => "List"
    MapValue(_) => "Map"
    SequenceValue(_) => "Sequence"
    FunctionValue(_) => "Fn"
    FiberValue(_) => "Fiber"
    ClassValue(_) => "Class"
    MetaClassValue(_) => "Class"
    ClassAttributesValue(_, _) => "ClassAttributes"
    InstanceValue(_) => "Instance"
    SuperValue(_, _, _, _) => "Super"
    SuperClassValue(_, _) => "SuperClass"
    CoreClassValue(_) => "Class"
    IteratorValue(_) => "Iterator"
    MapEntryValue(_) => "MapEntry"
    SystemValue => "System"
  }
}

///|
/// Convert a primitive value to its Wren-facing textual representation.
pub fn Value::to_text(self : Value) -> String {
  match self {
    NullValue => "null"
    BoolValue(true) => "true"
    BoolValue(false) => "false"
    NumberValue(number) => wren_number_to_text(number)
    StringValue(text) => text
    RawStringValue(_) => "�"
    RangeValue(from, to, true) =>
      wren_number_to_text(from) + ".." + wren_number_to_text(to)
    RangeValue(from, to, false) =>
      wren_number_to_text(from) + "..." + wren_number_to_text(to)
    ListValue(_) => ""
    MapValue(_) => ""
    SequenceValue(_) => ""
    FunctionValue(_) => ""
    FiberValue(_) => ""
    ClassValue(_) => ""
    MetaClassValue(name) => name
    ClassAttributesValue(_, _) => ""
    InstanceValue(_) => ""
    SuperValue(_, _, _, _) => ""
    SuperClassValue(_, _) => ""
    CoreClassValue(class_value) => class_value.name()
    IteratorValue(_) => ""
    MapEntryValue(_) => ""
    SystemValue => "System"
  }
}