///|
// Constant type
pub(all) enum ConType {
  CUndef
  CBits // Numeric constant
  CAddr // Symbol address
} derive(Eq, Debug)

///|
// Constant value bits
pub(all) struct ConBits {
  i : Int64 // Integer value
  d : Double // Double value
  s : Float // Single (float) value
} derive(Debug, Eq)

///|
// Constant - corresponds to struct Con in QBE
pub(all) struct Con {
  kind : ConType
  label : Int // Interned string label for CAddr
  bits : ConBits // Value bits
  flt : Int // 0=integer, 1=print as s, 2=print as d
  is_local : Bool // True if local symbol
} derive(Debug, Eq)

///|
pub fn Con::new() -> Con {
  Con::{
    kind: CUndef,
    label: 0,
    bits: ConBits::{ i: 0, d: 0.0, s: 0.0, },
    flt: 0,
    is_local: false,
  }
}

///|
pub fn Con::int(i : Int64) -> Con {
  Con::{
    kind: CBits,
    label: 0,
    bits: ConBits::{ i, d: 0.0, s: 0.0, },
    flt: 0,
    is_local: false,
  }
}

///|
pub fn Con::double(d : Double) -> Con {
  Con::{
    kind: CBits,
    label: 0,
    bits: ConBits::{ i: 0, d, s: 0.0, },
    flt: 2,
    is_local: false,
  }
}

///|
pub fn Con::single(s : Float) -> Con {
  Con::{
    kind: CBits,
    label: 0,
    bits: ConBits::{ i: 0, d: 0.0, s, },
    flt: 1,
    is_local: false,
  }
}

///|
pub fn Con::addr(label : Int) -> Con {
  Con::{
    kind: CAddr,
    label,
    bits: ConBits::{ i: 0, d: 0.0, s: 0.0, },
    flt: 0,
    is_local: false,
  }
}

///|
// Check if constant is zero
pub fn Con::is_zero(self : Con, wide : Bool) -> Bool {
  if self.kind != CBits {
    return false
  }
  if wide {
    self.bits.i == 0
  } else {
    (self.bits.i & 0xFFFFFFFF) == 0
  }
}

///|
// Add two constants (for address arithmetic), matching C addcon in util.c
#as_free_fn(addcon, deprecated="use `Con::add` instead")
pub fn Con::add(self : Con, other : Con) -> Con {
  if self.kind == CUndef {
    other
  } else {
    let kind = if other.kind == CAddr { CAddr } else { self.kind }
    let label = if other.kind == CAddr { other.label } else { self.label }
    Con::{
      kind,
      label,
      bits: ConBits::{
        i: self.bits.i + other.bits.i,
        d: self.bits.d,
        s: self.bits.s,
      },
      flt: self.flt,
      is_local: self.is_local,
    }
  }
}

///|
// Raw bit representation of a constant (for interning, like C's union)
#as_free_fn(con_raw_bits, deprecated="use `Con::raw_bits` instead")
pub fn Con::raw_bits(self : Con) -> Int64 {
  if self.kind == CBits && self.flt == 1 {
    self.bits.s.reinterpret_as_uint().to_int64()
  } else if self.kind == CBits && self.flt == 2 {
    self.bits.d.reinterpret_as_int64()
  } else {
    self.bits.i
  }
}

///|
// Interning comparison: type + raw bits + label (ignores flt flag),
// matching C's `con[i].type == c.type && con[i].bits.i == c.bits.i`
fn con_eq(c : Con, other : Con) -> Bool {
  c.kind == other.kind &&
  c.raw_bits() == other.raw_bits() &&
  c.label == other.label
}