///|
/// Convert a VCD scalar character to its classified four-state value.
pub fn logic_bit(value : Char) -> LogicBit {
  match value {
    '0' => Zero
    '1' => One
    'x' | 'X' => Unknown
    'z' | 'Z' => HighImpedance
    _ => Unknown
  }
}

///|
/// Convert a classified logic state to canonical VCD spelling.
pub fn LogicBit::to_char(self : LogicBit) -> Char {
  match self {
    Zero => '0'
    One => '1'
    Unknown => 'x'
    HighImpedance => 'z'
  }
}

///|
/// Return a stable human-readable representation of any VCD value.
pub fn Value::display(self : Value) -> String {
  match self {
    Scalar(value) => value.to_string()
    Vector(bits) => "b" + bits
    Real(number) => "r" + number.to_string()
  }
}

///|
/// Return the number of bits represented by a VCD value.
pub fn Value::width(self : Value) -> Int {
  match self {
    Scalar(_) => 1
    Vector(bits) => bits.length()
    Real(_) => 0
  }
}

///|
/// Return the scalar character when the value is a scalar.
pub fn Value::as_scalar(self : Value) -> Char? {
  match self {
    Scalar(value) => Some(value)
    _ => None
  }
}

///|
/// Return whether a value contains an unknown or high-impedance bit.
pub fn Value::is_unknown(self : Value) -> Bool {
  match self {
    Scalar(value) =>
      match logic_bit(value) {
        Unknown | HighImpedance => true
        _ => false
      }
    Vector(bits) => {
      for bit in bits {
        match logic_bit(bit) {
          Unknown | HighImpedance => return true
          _ => ()
        }
      }
      false
    }
    Real(_) => false
  }
}

///|
/// Return a canonical lowercase representation without changing numeric data.
pub fn Value::normalized(self : Value) -> Value {
  match self {
    Scalar(value) => Scalar(logic_bit(value).to_char())
    Vector(bits) => Vector(bits.to_lower())
    Real(number) => Real(number)
  }
}

///|
/// Expand a value into classified bits, preserving vector order.
pub fn Value::bits(self : Value) -> Array[LogicBit] {
  match self {
    Scalar(value) => [logic_bit(value)]
    Vector(text) => text.to_array().map(logic_bit)
    Real(_) => []
  }
}

///|
/// Convert classified bits to a canonical vector string.
pub fn bits_to_string(bits : Array[LogicBit]) -> String {
  bits.map(fn(bit) { bit.to_char().to_string() }).join("")
}

///|
/// Convert a binary VCD value to an integer, returning None for unknown bits.
pub fn Value::to_int(self : Value) -> Int64? {
  let bits = self.bits()
  if bits.length() == 0 {
    return None
  }
  let mut result : Int64 = 0L
  for bit in bits {
    match bit {
      Zero => result = result * 2L
      One => result = result * 2L + 1L
      _ => return None
    }
  }
  Some(result)
}

///|
/// Apply a four-state binary operation to two values after width extension.
pub fn value_binary_op(left : Value, right : Value, op : String) -> Value {
  let a = left.bits()
  let b = right.bits()
  let width = Int::max(a.length(), b.length())
  if width == 0 {
    return Vector("")
  }
  let result : Array[LogicBit] = []
  for i in 0.. Value {
  value_binary_op(left, right, "and")
}

///|
/// Four-state bitwise OR.
pub fn value_or(left : Value, right : Value) -> Value {
  value_binary_op(left, right, "or")
}

///|
/// Four-state bitwise XOR.
pub fn value_xor(left : Value, right : Value) -> Value {
  value_binary_op(left, right, "xor")
}

///|
/// Four-state bitwise NOT.
pub fn value_not(value : Value) -> Value {
  let bits = value
    .bits()
    .map(fn(bit) {
      match bit {
        Zero => One
        One => Zero
        _ => Unknown
      }
    })
  if value.width() == 1 {
    Scalar(bits[0].to_char())
  } else {
    Vector(bits_to_string(bits))
  }
}

///|
fn aligned_bit(bits : Array[LogicBit], width : Int, index : Int) -> LogicBit {
  let offset = width - bits.length()
  if index < offset {
    Zero
  } else {
    bits[index - offset]
  }
}

///|
fn logic_binary_op(left : LogicBit, right : LogicBit, op : String) -> LogicBit {
  match op {
    "and" =>
      match (left, right) {
        (Zero, _) | (_, Zero) => Zero
        (One, One) => One
        _ => Unknown
      }
    "or" =>
      match (left, right) {
        (One, _) | (_, One) => One
        (Zero, Zero) => Zero
        _ => Unknown
      }
    _ =>
      match (left, right) {
        (Zero, Zero) | (One, One) => Zero
        (Zero, One) | (One, Zero) => One
        _ => Unknown
      }
  }
}

///|
/// Return the canonical scalar category used by edge analysis.
pub fn edge_kind(before : Char, after : Char) -> EdgeKind {
  match (logic_bit(before), logic_bit(after)) {
    (Zero, One) => Rising
    (One, Zero) => Falling
    (_, Unknown) => ToUnknown
    (Unknown, _) => FromUnknown
    (_, HighImpedance) => ToHighImpedance
    (HighImpedance, _) => FromHighImpedance
    _ => Other
  }
}

///|
/// Return whether a scalar character represents a known driven level.
pub fn is_known_scalar(value : Char) -> Bool {
  match logic_bit(value) {
    Zero | One => true
    _ => false
  }
}

///|
/// Return whether two values are equal after VCD case normalization.
pub fn values_equal(
  left : Value,
  right : Value,
  normalize_unknown : Bool,
) -> Bool {
  if normalize_unknown {
    left.normalized() == right.normalized()
  } else {
    left == right
  }
}