///|
/// Local MoonECAT CiA 402 adapter for offline Isochronon conformance fixtures.

///|
pub fn package_id() -> String {
  "fieldbus_core/moonecat_cia402_adapter"
}

///|
pub(all) struct MoonEcatCia402Asset {
  object_id : String
  label : String
  index : UInt
  subindex : Byte
  channel : String
  source_path : String
  source_symbol : String
  behavior : String
} derive(Eq, Debug)

///|
pub fn moonecat_cia402_assets() -> Array[MoonEcatCia402Asset] {
  [
    {
      object_id: "axis.controlword",
      label: "cia402.controlword.write",
      index: @profile_cia402.cia402_controlword_index,
      subindex: b'\x00',
      channel: "sdo/rxpdo",
      source_path: "MoonECAT/runtime/run.mbt",
      source_symbol: "derive_startup_outputs_with_commands",
      behavior: "writes 0x0006 to 0x6040:0 when DS402 channels expose a 16-bit controlword in RxPDO",
    },
    {
      object_id: "axis.statusword",
      label: "cia402.statusword.read",
      index: @profile_cia402.cia402_statusword_index,
      subindex: b'\x00',
      channel: "sdo/txpdo",
      source_path: "MoonECAT/protocol/integration_test.mbt",
      source_symbol: "sdo_upload_retry 0x6041",
      behavior: "uploads 0x6041:0 statusword through CoE SDO retry fixtures and maps it in TxPDO fixtures",
    },
    {
      object_id: "axis.mode",
      label: "cia402.mode.write",
      index: @profile_cia402.cia402_mode_of_operation_index,
      subindex: b'\x00',
      channel: "sdo/rxpdo",
      source_path: "MoonECAT/runtime/run.mbt",
      source_symbol: "preferred_ds402_mode_seed",
      behavior: "selects 0x08 profile position mode when 0x607A:0 target position is present in RxPDO",
    },
    {
      object_id: "axis.target_position",
      label: "cia402.target_position.write",
      index: @profile_cia402.cia402_target_position_index,
      subindex: b'\x00',
      channel: "rxpdo",
      source_path: "MoonECAT/mailbox/mailbox_test.mbt",
      source_symbol: "SiiPdoEntry 0x607A",
      behavior: "records 0x607A:0 target position as a 32-bit RxPDO entry",
    },
    {
      object_id: "axis.actual_position",
      label: "cia402.actual_position.read",
      index: @profile_cia402.cia402_actual_position_index,
      subindex: b'\x00',
      channel: "txpdo",
      source_path: "MoonECAT/mailbox/mailbox_test.mbt",
      source_symbol: "SiiPdoEntry 0x6064",
      behavior: "records 0x6064:0 actual position as a 32-bit TxPDO entry",
    },
    {
      object_id: "axis.fault",
      label: "cia402.fault.detected",
      index: @profile_cia402.cia402_fault_index,
      subindex: b'\x00',
      channel: "profile-event",
      source_path: "MoonECAT/runtime/run.mbt",
      source_symbol: "RunFault",
      behavior: "normalizes MoonECAT runtime fault surface to a profile-level axis.fault event; MoonECAT does not currently expose 0x603F directly",
    },
  ]
}

///|
pub fn moonecat_cia402_bindings() -> Array[@isocontract.ObjectBinding] {
  @profile_cia402.cia402_coe_bindings()
}

///|
pub fn moonecat_cia402_contracts() -> @isocontract.ContractRegistry {
  @profile_cia402.cia402_object_contracts()
}

///|
pub fn moonecat_cia402_protocol() -> @protocol_ast.GlobalProtocol {
  @profile_cia402.cia402_enable_fault_reset_protocol()
}

///|
pub fn moonecat_cia402_observer_backend() -> @protocol_ast.ObserverBackend {
  @protocol_ast.ObserverBackend::new(
    registry=moonecat_cia402_contracts(),
    protocol=moonecat_cia402_protocol(),
    roles=[@protocol_ast.role(name="controller")],
  )
}

///|
pub fn moonecat_cia402_trace_fixture() -> @trace.TraceLog {
  let log = @trace.TraceLog::new()
  log.append(
    adapter_event(1, "cia402.controlword.write", @trace.Tx, 0L, Some(0x0006)),
  )
  log.append(
    adapter_event(2, "cia402.statusword.read", @trace.Rx, 10L, Some(0x0021)),
  )
  log.append(adapter_event(3, "cia402.mode.write", @trace.Tx, 20L, Some(0x08)))
  log.append(
    adapter_event(
      4,
      "cia402.target_position.write",
      @trace.Tx,
      30L,
      Some(607_000),
    ),
  )
  log.append(
    adapter_event(
      5,
      "cia402.actual_position.read",
      @trace.Rx,
      40L,
      Some(606_400),
    ),
  )
  log.append(adapter_event(6, "cia402.fault.detected", @trace.Rx, 50L, Some(1)))
  log.append(
    adapter_event(7, "cia402.controlword.write", @trace.Tx, 60L, Some(0x0080)),
  )
  log.append(
    adapter_event(8, "cia402.statusword.read", @trace.Rx, 70L, Some(0x0040)),
  )
  log
}

///|
pub fn moonecat_cia402_observer_report() -> @protocol_ast.ConformanceReport {
  moonecat_cia402_observer_backend().check_trace(
    moonecat_cia402_trace_fixture(),
  )
}

///|
fn adapter_event(
  event_id : Int,
  label : String,
  direction : @trace.TraceDirection,
  time_ns : Int64,
  payload_digest : Int?,
) -> @trace.TraceEvent {
  @trace.TraceEvent::make(
    event_id~,
    parent_id=None,
    vtime=@core.VTime::from_ns(time_ns),
    clock_domain="sim",
    raw_ns=time_ns,
    node_id="controller",
    medium_id="trace",
    channel_id=None,
    direction~,
    payload_digest~,
    rng_step=event_id,
    seed=402,
    backend=@core.SimNative,
    label~,
  )
}