///|
/// HAL capability descriptors for embedded and link-port profile gates.

///|
pub fn package_id() -> String {
  "lockwire/hal"
}

///|
pub(all) enum HalCapabilityKind {
  HalVirtualClock
  HalInterrupt
  HalDma
  HalLinkPort
  HalMac
  HalCanFd
  HalUart
  HalFramePool
} derive(Eq, Debug)

///|
pub fn HalCapabilityKind::label(self : HalCapabilityKind) -> String {
  match self {
    HalVirtualClock => "virtual-clock"
    HalInterrupt => "interrupt"
    HalDma => "dma"
    HalLinkPort => "link-port"
    HalMac => "mac"
    HalCanFd => "can-fd"
    HalUart => "uart"
    HalFramePool => "frame-pool"
  }
}

///|
pub(all) struct HalCapabilityDescriptor {
  id : String
  kind : HalCapabilityKind
  profiles : Array[@os.CapabilityProfile]
  requires_irq : Bool
  requires_dma : Bool
  requires_link_port : Bool
  requires_c_ffi : Bool
  deterministic : Bool
  live_io : Bool
  reference_path : String
} derive(Eq, Debug)

///|
pub fn HalCapabilityDescriptor::supports_profile(
  self : HalCapabilityDescriptor,
  profile : @os.CapabilityProfile,
) -> Bool {
  self.profiles.any(fn(item) { item == profile })
}

///|
pub fn HalCapabilityDescriptor::violates_wasm_boundary(
  self : HalCapabilityDescriptor,
) -> Bool {
  self.supports_profile(@os.ProfileSimWasm) &&
  (
    self.requires_c_ffi ||
    self.requires_irq ||
    self.requires_dma ||
    self.live_io
  )
}

///|
pub fn HalCapabilityDescriptor::requires_physical_hal(
  self : HalCapabilityDescriptor,
) -> Bool {
  self.requires_irq ||
  self.requires_dma ||
  self.requires_link_port ||
  self.live_io
}

///|
pub(all) struct HalCapabilityReport {
  catalog_count : Int
  sim_wasm_count : Int
  real_embedded_count : Int
  irq_count : Int
  dma_count : Int
  link_port_count : Int
  native_frame_pool_count : Int
  deterministic_sim_count : Int
  wasm_violation_count : Int
} derive(Eq, Debug)

///|
pub fn HalCapabilityReport::passes(self : HalCapabilityReport) -> Bool {
  self.catalog_count >= 8 &&
  self.sim_wasm_count >= 2 &&
  self.real_embedded_count >= 5 &&
  self.irq_count >= 3 &&
  self.dma_count >= 2 &&
  self.link_port_count >= 3 &&
  self.native_frame_pool_count >= 1 &&
  self.deterministic_sim_count >= 2 &&
  self.wasm_violation_count == 0
}

///|
pub fn hal_capability_catalog() -> Array[HalCapabilityDescriptor] {
  [
    {
      id: "hal.clock.virtual",
      kind: HalVirtualClock,
      profiles: [@os.ProfileSimNative, @os.ProfileSimWasm, @os.ProfileReplay],
      requires_irq: false,
      requires_dma: false,
      requires_link_port: false,
      requires_c_ffi: false,
      deterministic: true,
      live_io: false,
      reference_path: "lockwire/clock/clock.mbt",
    },
    {
      id: "hal.link_port.sim_loopback",
      kind: HalLinkPort,
      profiles: [@os.ProfileSimNative, @os.ProfileSimWasm, @os.ProfileReplay],
      requires_irq: false,
      requires_dma: false,
      requires_link_port: false,
      requires_c_ffi: false,
      deterministic: true,
      live_io: false,
      reference_path: "lockwire/sim/sim.mbt",
    },
    {
      id: "hal.frame_pool.native",
      kind: HalFramePool,
      profiles: [@os.ProfileNativeReal, @os.ProfileRealLinux],
      requires_irq: false,
      requires_dma: false,
      requires_link_port: false,
      requires_c_ffi: true,
      deterministic: false,
      live_io: false,
      reference_path: "lockwire/driver/native/stub_frame_pool.h + stub_frame_pool_*.c",
    },
    {
      id: "hal.clock.embedded",
      kind: HalVirtualClock,
      profiles: [@os.ProfileRealEmbedded],
      requires_irq: false,
      requires_dma: false,
      requires_link_port: false,
      requires_c_ffi: false,
      deterministic: false,
      live_io: true,
      reference_path: "docs/lockwire-plan.md#real-embedded",
    },
    {
      id: "hal.irq.embedded",
      kind: HalInterrupt,
      profiles: [@os.ProfileRealEmbedded],
      requires_irq: true,
      requires_dma: false,
      requires_link_port: false,
      requires_c_ffi: false,
      deterministic: false,
      live_io: true,
      reference_path: "docs/lockwire-plan.md#threadx-hal",
    },
    {
      id: "hal.dma.embedded",
      kind: HalDma,
      profiles: [@os.ProfileRealEmbedded],
      requires_irq: true,
      requires_dma: true,
      requires_link_port: false,
      requires_c_ffi: false,
      deterministic: false,
      live_io: true,
      reference_path: "docs/lockwire-plan.md#dma",
    },
    {
      id: "hal.mac.link_port.embedded",
      kind: HalMac,
      profiles: [@os.ProfileRealEmbedded],
      requires_irq: true,
      requires_dma: true,
      requires_link_port: true,
      requires_c_ffi: false,
      deterministic: false,
      live_io: true,
      reference_path: "docs/lockwire-plan.md#mac-hal",
    },
    {
      id: "hal.canfd.link_port.embedded",
      kind: HalCanFd,
      profiles: [@os.ProfileRealEmbedded],
      requires_irq: true,
      requires_dma: false,
      requires_link_port: true,
      requires_c_ffi: false,
      deterministic: false,
      live_io: true,
      reference_path: "docs/lockwire-plan.md#can-fd-hal",
    },
    {
      id: "hal.uart.link_port.embedded",
      kind: HalUart,
      profiles: [@os.ProfileRealEmbedded],
      requires_irq: true,
      requires_dma: false,
      requires_link_port: true,
      requires_c_ffi: false,
      deterministic: false,
      live_io: true,
      reference_path: "docs/lockwire-plan.md#uart-hal",
    },
    {
      id: "hal.frame_pool.embedded",
      kind: HalFramePool,
      profiles: [@os.ProfileRealEmbedded],
      requires_irq: false,
      requires_dma: true,
      requires_link_port: true,
      requires_c_ffi: false,
      deterministic: false,
      live_io: true,
      reference_path: "docs/lockwire-plan.md#embedded-native",
    },
  ]
}

///|
pub fn hal_capability_report() -> HalCapabilityReport {
  let catalog = hal_capability_catalog()
  {
    catalog_count: catalog.length(),
    sim_wasm_count: count_profile(catalog, @os.ProfileSimWasm),
    real_embedded_count: count_profile(catalog, @os.ProfileRealEmbedded),
    irq_count: count_where(catalog, fn(item) { item.requires_irq }),
    dma_count: count_where(catalog, fn(item) { item.requires_dma }),
    link_port_count: count_where(catalog, fn(item) { item.requires_link_port }),
    native_frame_pool_count: count_where(catalog, fn(item) {
      item.kind == HalFramePool && item.requires_c_ffi
    }),
    deterministic_sim_count: count_where(catalog, fn(item) {
      item.deterministic &&
      item.supports_profile(@os.ProfileSimWasm) &&
      !item.live_io
    }),
    wasm_violation_count: count_where(catalog, fn(item) {
      item.violates_wasm_boundary()
    }),
  }
}

///|
fn count_profile(
  catalog : ArrayView[HalCapabilityDescriptor],
  profile : @os.CapabilityProfile,
) -> Int {
  count_where(catalog, fn(item) { item.supports_profile(profile) })
}

///|
fn count_where(
  catalog : ArrayView[HalCapabilityDescriptor],
  predicate : (HalCapabilityDescriptor) -> Bool,
) -> Int {
  let mut count = 0
  for item in catalog {
    if predicate(item) {
      count += 1
    }
  }
  count
}