///|
/// Reactor backend profile descriptors for Lockwire.

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

///|
pub(all) enum ReactorBackendKind {
  ReactorSim
  ReactorEpoll
  ReactorIocp
  ReactorThreadx
} derive(Eq, Debug)

///|
pub fn ReactorBackendKind::label(self : ReactorBackendKind) -> String {
  match self {
    ReactorSim => "reactor_sim"
    ReactorEpoll => "reactor_epoll"
    ReactorIocp => "reactor_iocp"
    ReactorThreadx => "reactor_threadx"
  }
}

///|
pub(all) enum ReactorBoundary {
  ReactorDeterministic
  ReactorHostReal
  ReactorEmbeddedReal
} derive(Eq, Debug)

///|
pub fn ReactorBoundary::label(self : ReactorBoundary) -> String {
  match self {
    ReactorDeterministic => "deterministic"
    ReactorHostReal => "host-real"
    ReactorEmbeddedReal => "embedded-real"
  }
}

///|
pub(all) struct ReactorBackendDescriptor {
  id : String
  kind : ReactorBackendKind
  boundary : ReactorBoundary
  profiles : Array[@os.CapabilityProfile]
  requires_native : Bool
  requires_c_ffi : Bool
  requires_os_fd : Bool
  requires_threadx : Bool
  deterministic : Bool
  live_runtime : Bool
  package_path : String
  reference_path : String
} derive(Eq, Debug)

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

///|
pub fn ReactorBackendDescriptor::violates_wasm_boundary(
  self : ReactorBackendDescriptor,
) -> Bool {
  self.supports_profile(@os.ProfileSimWasm) &&
  (
    self.requires_native ||
    self.requires_c_ffi ||
    self.requires_os_fd ||
    self.requires_threadx ||
    self.live_runtime
  )
}

///|
pub fn ReactorBackendDescriptor::is_real_only(
  self : ReactorBackendDescriptor,
) -> Bool {
  self.live_runtime ||
  self.requires_native ||
  self.requires_os_fd ||
  self.requires_threadx
}

///|
pub(all) struct ReactorBackendReport {
  catalog_count : Int
  sim_backend_count : Int
  sim_wasm_count : Int
  deterministic_count : Int
  native_real_count : Int
  real_linux_count : Int
  real_embedded_count : Int
  os_fd_count : Int
  c_ffi_count : Int
  live_runtime_count : Int
  threadx_count : Int
  wasm_violation_count : Int
} derive(Eq, Debug)

///|
pub fn ReactorBackendReport::passes(self : ReactorBackendReport) -> Bool {
  self.catalog_count == 4 &&
  self.sim_backend_count == 1 &&
  self.sim_wasm_count == 1 &&
  self.deterministic_count == 1 &&
  self.native_real_count == 1 &&
  self.real_linux_count == 1 &&
  self.real_embedded_count == 1 &&
  self.os_fd_count == 2 &&
  self.c_ffi_count == 2 &&
  self.live_runtime_count == 3 &&
  self.threadx_count == 1 &&
  self.wasm_violation_count == 0
}

///|
pub fn reactor_backend_catalog() -> Array[ReactorBackendDescriptor] {
  [
    {
      id: "reactor.sim",
      kind: ReactorSim,
      boundary: ReactorDeterministic,
      profiles: [@os.ProfileSimNative, @os.ProfileSimWasm, @os.ProfileReplay],
      requires_native: false,
      requires_c_ffi: false,
      requires_os_fd: false,
      requires_threadx: false,
      deterministic: true,
      live_runtime: false,
      package_path: "lockwire/reactor",
      reference_path: "lockwire/scenario/scenario.mbt",
    },
    {
      id: "reactor.epoll",
      kind: ReactorEpoll,
      boundary: ReactorHostReal,
      profiles: [@os.ProfileRealLinux],
      requires_native: true,
      requires_c_ffi: true,
      requires_os_fd: true,
      requires_threadx: false,
      deterministic: false,
      live_runtime: true,
      package_path: "lockwire/reactor/epoll",
      reference_path: "docs/Isochronon-architecture.md#reactor_epoll",
    },
    {
      id: "reactor.iocp",
      kind: ReactorIocp,
      boundary: ReactorHostReal,
      profiles: [@os.ProfileNativeReal],
      requires_native: true,
      requires_c_ffi: true,
      requires_os_fd: true,
      requires_threadx: false,
      deterministic: false,
      live_runtime: true,
      package_path: "lockwire/reactor/iocp",
      reference_path: "docs/clock-layer.md#async",
    },
    {
      id: "reactor.threadx",
      kind: ReactorThreadx,
      boundary: ReactorEmbeddedReal,
      profiles: [@os.ProfileRealEmbedded],
      requires_native: true,
      requires_c_ffi: false,
      requires_os_fd: false,
      requires_threadx: true,
      deterministic: false,
      live_runtime: true,
      package_path: "lockwire/reactor/threadx",
      reference_path: "docs/clock-layer.md#threadx",
    },
  ]
}

///|
pub fn reactor_backend_report() -> ReactorBackendReport {
  let catalog = reactor_backend_catalog()
  {
    catalog_count: catalog.length(),
    sim_backend_count: count_where(catalog, fn(item) { item.kind == ReactorSim }),
    sim_wasm_count: count_profile(catalog, @os.ProfileSimWasm),
    deterministic_count: count_where(catalog, fn(item) { item.deterministic }),
    native_real_count: count_profile(catalog, @os.ProfileNativeReal),
    real_linux_count: count_profile(catalog, @os.ProfileRealLinux),
    real_embedded_count: count_profile(catalog, @os.ProfileRealEmbedded),
    os_fd_count: count_where(catalog, fn(item) { item.requires_os_fd }),
    c_ffi_count: count_where(catalog, fn(item) { item.requires_c_ffi }),
    live_runtime_count: count_where(catalog, fn(item) { item.live_runtime }),
    threadx_count: count_where(catalog, fn(item) { item.requires_threadx }),
    wasm_violation_count: count_where(catalog, fn(item) {
      item.violates_wasm_boundary()
    }),
  }
}

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

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