///|
/// High-level parsed representation of a component.
///
/// This is intentionally partial: for now we only parse a subset of sections
/// (types/imports/exports) while keeping the raw binary sections available for
/// later instantiation work.

///|
pub(all) struct Component {
  binary : ComponentBinary
  core_modules : Array[Bytes]
  core_instances : Array[Bytes]
  core_types : Array[Bytes]
  types : Array[TypeDef]
  imports : Array[Import]
  exports : Array[Export]
  instances : Array[Instance]
  aliases : Array[Alias]
  canons : Array[Canon]
  start : Start?
  components : Array[Component]
} derive(Debug, Eq)

///|
/// Keep `Show` behavior while migrating from deprecated `derive(Show)` to
/// `derive(Debug)`.
pub impl Show for Component with fn output(self, logger) {
  logger.write_string(@types.compact_show_repr(Repr(self).to_string()))
}

///|
pub fn parse_component(bytes : Bytes) -> Component raise ComponentParseError {
  component_from_binary(parse_component_binary(bytes))
}

///|
/// Encode the component's immutable binary snapshot.
///
/// `Bytes` is immutable, so validation evidence can retain and expose this
/// value without granting mutation access to the parsed component graph.
pub fn Component::snapshot_bytes(self : Component) -> Bytes {
  let bytes : Array[Byte] = [b'\x00', b'a', b's', b'm']
  bytes.push((self.binary.version & 0xFF).to_byte())
  bytes.push(((self.binary.version >> 8) & 0xFF).to_byte())
  bytes.push((self.binary.layer & 0xFF).to_byte())
  bytes.push(((self.binary.layer >> 8) & 0xFF).to_byte())
  for section in self.binary.sections {
    bytes.push(section.id.to_byte())
    append_component_leb_u32(bytes, section.payload.length())
    for byte in section.payload {
      bytes.push(byte)
    }
  }
  Bytes::from_array(bytes)
}

///|
fn append_component_leb_u32(bytes : Array[Byte], value : Int) -> Unit {
  for remaining = value {
    let byte = remaining & 0x7F
    let next = remaining >> 7
    if next == 0 {
      bytes.push(byte.to_byte())
      break
    }
    bytes.push((byte | 0x80).to_byte())
    continue next
  }
}

///|
/// Rebuild a component that shares no mutable state with `self`.
pub fn Component::isolated_copy(
  self : Component,
) -> Component raise ComponentParseError {
  parse_component(self.snapshot_bytes())
}

///|
fn component_from_binary(
  binary : ComponentBinary,
) -> Component raise ComponentParseError {
  let core_modules : Array[Bytes] = []
  let core_instances : Array[Bytes] = []
  let core_types : Array[Bytes] = []
  let types : Array[TypeDef] = []
  let imports : Array[Import] = []
  let exports : Array[Export] = []
  let instances : Array[Instance] = []
  let aliases : Array[Alias] = []
  let canons : Array[Canon] = []
  let mut start : Start? = None
  let components : Array[Component] = []
  for s in binary.sections {
    if s.id == 1 {
      core_modules.push(s.payload)
    } else if s.id == 2 {
      core_instances.push(s.payload)
    } else if s.id == 3 {
      core_types.push(s.payload)
    } else if s.id == 7 {
      for t in parse_type_section(s.payload) {
        types.push(t)
      }
    } else if s.id == 10 {
      for i in parse_import_section(s.payload) {
        imports.push(i)
      }
    } else if s.id == 11 {
      for e in parse_export_section(s.payload) {
        exports.push(e)
      }
    } else if s.id == 5 {
      for inst in parse_instance_section(s.payload) {
        instances.push(inst)
      }
    } else if s.id == 6 {
      for a in parse_alias_section(s.payload) {
        aliases.push(a)
      }
    } else if s.id == 8 {
      for c in parse_canon_section(s.payload) {
        canons.push(c)
      }
    } else if s.id == 9 {
      start = Some(parse_start_section(s.payload))
    } else if s.id == 4 {
      components.push(parse_component(s.payload))
    } else {
      ()
    }
  }
  {
    binary,
    core_modules,
    core_instances,
    core_types,
    types,
    imports,
    exports,
    instances,
    aliases,
    canons,
    start,
    components,
  }
}