///|
/// CoSimAdapter external process bridge.
///
/// C-11: Bridges MoonECAT's CoSimAdapter trait to an external process
/// (Simulink, Modelica, custom C model) via:
///   - Unix domain socket (Linux/macOS)
///   - Named pipe (Windows)
///   - Shared memory + semaphore (low-latency variant)
///
/// Wire protocol (per step):
///   Request:  [8B sim_time_ns][8B dt_ns][NB outputs_data]
///   Response: [1B status][NB inputs_data][optional: 8B new_sim_time]
///
/// Status byte values:
///   0x00 = OK
///   0x01 = Terminated (followed by NUL-terminated reason string)
///   0xFF = Error (followed by NUL-terminated message)

///|
/// Transport type for the external process bridge.
pub(all) enum BridgeTransport {
  /// Unix domain socket at the given path.
  UnixSocket(String)
  /// Named pipe (Windows).
  NamedPipe(String)
  /// Shared memory region + semaphore pair.
  SharedMemory(name~ : String, size~ : Int)
} derive(Debug)

///|
/// Configuration for the external process bridge.
pub(all) struct ExternalBridgeConfig {
  /// Transport mechanism.
  transport : BridgeTransport
  /// Timeout per step call in nanoseconds (0 = no timeout).
  step_timeout_ns : Int64
  /// Whether to launch the external process automatically.
  auto_launch : Bool
  /// Command to launch the external process (if auto_launch).
  launch_command : String
  /// Working directory for the external process.
  launch_cwd : String
} derive(Debug)

///|
pub fn ExternalBridgeConfig::unix_socket(path : String) -> ExternalBridgeConfig {
  {
    transport: UnixSocket(path),
    step_timeout_ns: 1_000_000_000L,
    auto_launch: false,
    launch_command: "",
    launch_cwd: "",
  }
}

///|
pub fn ExternalBridgeConfig::named_pipe(name : String) -> ExternalBridgeConfig {
  {
    transport: NamedPipe(name),
    step_timeout_ns: 1_000_000_000L,
    auto_launch: false,
    launch_command: "",
    launch_cwd: "",
  }
}

///|
pub fn ExternalBridgeConfig::shared_memory(
  name : String,
  size : Int,
) -> ExternalBridgeConfig {
  {
    transport: SharedMemory(name~, size~),
    step_timeout_ns: 500_000_000L,
    auto_launch: false,
    launch_command: "",
    launch_cwd: "",
  }
}

///|
/// Connection state of the external bridge.
pub(all) enum BridgeState {
  /// Not yet connected.
  Disconnected
  /// Connected and ready for step calls.
  Connected
  /// External process terminated.
  Terminated(String)
  /// Communication error occurred.
  Error(String)
} derive(Eq, Debug)

///|
/// ExternalProcessBridge — CoSimAdapter that delegates to an external process.
///
/// This struct manages the connection lifecycle and serialization.
/// Actual I/O operations are delegated to platform-specific extern "c" stubs.
pub(all) struct ExternalProcessBridge {
  config : ExternalBridgeConfig
  mut state : BridgeState
  /// Output buffer size (from CoSimConfig).
  mut output_size : Int
  /// Input buffer size (from CoSimConfig).
  mut input_size : Int
  /// Accumulated round-trip latencies for diagnostics.
  mut step_count : Int64
  mut total_latency_ns : Int64
} derive(Debug)

///|
pub fn ExternalProcessBridge::new(
  config : ExternalBridgeConfig,
) -> ExternalProcessBridge {
  {
    config,
    state: Disconnected,
    output_size: 0,
    input_size: 0,
    step_count: 0L,
    total_latency_ns: 0L,
  }
}

///|
/// Average round-trip latency in nanoseconds.
pub fn ExternalProcessBridge::avg_latency_ns(
  self : ExternalProcessBridge,
) -> Int64 {
  if self.step_count == 0L {
    0L
  } else {
    self.total_latency_ns / self.step_count
  }
}

///|
/// Encode a step request into the wire protocol format.
///
/// Format: [8B sim_time_ns LE][8B dt_ns LE][output_size B outputs]
pub fn encode_step_request(
  sim_time_ns : Int64,
  dt_ns : Int64,
  outputs : Bytes,
  output_size : Int,
) -> Bytes {
  let total = 16 + output_size
  let copy_len = if outputs.length() < output_size {
    outputs.length()
  } else {
    output_size
  }
  Bytes::makei(total, fn(i) {
    if i < 8 {
      (sim_time_ns >> (i * 8)).to_byte()
    } else if i < 16 {
      (dt_ns >> ((i - 8) * 8)).to_byte()
    } else if i - 16 < copy_len {
      outputs[i - 16]
    } else {
      b'\x00'
    }
  })
}

///|
/// Decode a step response from the wire protocol format.
///
/// Format: [1B status][input_size B inputs][optional tail]
/// Returns: (status_byte, input_data)
pub fn decode_step_response(data : Bytes, input_size : Int) -> (Byte, Bytes) {
  if data.length() < 1 {
    return (b'\xFF', Bytes::make(input_size, b'\x00'))
  }
  let status = data[0]
  let has_data = data.length() >= 1 + input_size
  let inputs = Bytes::makei(input_size, fn(i) {
    if has_data {
      data[1 + i]
    } else {
      b'\x00'
    }
  })
  (status, inputs)
}