///|
/// lockwire native plugin for the live-transport boundary.
///
/// Extends the open `LiveTransportKind`/`LiveTransport` extenums declared in
/// `mokomoking2501/lockwire/live_transport` with the Windows-npcap and Linux-raw-socket
/// backends, and registers handler chains (in `init`) so the protocol adapters
/// can open/send/receive/close through the master `live_*` functions without
/// ever importing this package. A future STM32/ThreadX backend would do the
/// same from its own package — `+=` adds variants, `init` adds behaviour.

///|
pub(all) extenum @live.LiveTransportKind += {
  NpcapKind
  RawSocketKind
}

///|
/// Live handle carrying the concrete native session plus the local MAC and
/// live-opened flag captured at open time.
pub(all) extenum @live.LiveTransport += {
  NpcapLink(NativeNpcapLinkSession, Bytes, Bool)
  RawSocketLink(NativeRawSocketLinkSession, Bytes, Bool)
}

///|
fn native_link_err_code(err : NativeLinkError) -> Int {
  match err {
    SystemError(_, code, _) => code
  }
}

///|
fn native_link_err_msg(err : NativeLinkError) -> String {
  match err {
    SystemError(_, _, msg) => msg
  }
}

///|
fn native_npcap_open_config(
  open : @live.LiveTransportOpen,
) -> NativeLinkSessionConfig {
  let base = native_link_session_config(NativeSessionNpcap)
  {
    ..base,
    interface_name: open.interface_name,
    ethertype: open.ethertype,
    promiscuous: open.promiscuous,
    allow_live_open: open.allow_live,
    open_timeout_ms: open.open_timeout_ms,
    snaplen: open.snaplen,
  }
}

///|
fn native_raw_open_config(
  open : @live.LiveTransportOpen,
) -> NativeLinkSessionConfig {
  let base = native_link_session_config(NativeSessionRawSocket)
  {
    ..base,
    interface_name: open.interface_name,
    ethertype: open.ethertype,
    promiscuous: open.promiscuous,
    allow_live_open: open.allow_live,
    open_timeout_ms: open.open_timeout_ms,
  }
}

///|
fn native_open(
  open : @live.LiveTransportOpen,
) -> @live.LiveTransport? raise @live.LiveTransportError {
  match open.kind {
    NpcapKind => {
      let config = native_npcap_open_config(open)
      let session = NativeNpcapLinkSession::open(config) catch {
        err =>
          raise @live.LiveTransportError::IoFailed(
            "open",
            native_link_err_code(err),
            native_link_err_msg(err),
          )
      }
      let mac = native_npcap_local_mac(open.interface_name)
      Some(NpcapLink(session, mac, open.allow_live))
    }
    RawSocketKind => {
      let config = native_raw_open_config(open)
      let session = NativeRawSocketLinkSession::open(config) catch {
        err =>
          raise @live.LiveTransportError::IoFailed(
            "open",
            native_link_err_code(err),
            native_link_err_msg(err),
          )
      }
      let mac = native_raw_socket_local_mac(open.interface_name)
      Some(RawSocketLink(session, mac, open.allow_live))
    }
    _ => None
  }
}

///|
fn native_send(
  t : @live.LiveTransport,
  frame : Bytes,
) -> Int? raise @live.LiveTransportError {
  match t {
    NpcapLink(session, _, _) => {
      let n = session.send_frame(frame) catch {
        err =>
          raise @live.LiveTransportError::IoFailed(
            "send",
            native_link_err_code(err),
            native_link_err_msg(err),
          )
      }
      Some(n)
    }
    RawSocketLink(session, _, _) => {
      let n = session.send_frame(frame) catch {
        err =>
          raise @live.LiveTransportError::IoFailed(
            "send",
            native_link_err_code(err),
            native_link_err_msg(err),
          )
      }
      Some(n)
    }
    _ => None
  }
}

///|
fn native_receive(
  t : @live.LiveTransport,
  timeout_ms : Int,
) -> Bytes? raise @live.LiveTransportError {
  match t {
    NpcapLink(session, _, _) => {
      let frame = session.receive_frame(timeout_ms~) catch {
        err =>
          raise @live.LiveTransportError::IoFailed(
            "receive",
            native_link_err_code(err),
            native_link_err_msg(err),
          )
      }
      Some(frame)
    }
    RawSocketLink(session, _, _) => {
      let frame = session.receive_frame(timeout_ms~) catch {
        err =>
          raise @live.LiveTransportError::IoFailed(
            "receive",
            native_link_err_code(err),
            native_link_err_msg(err),
          )
      }
      Some(frame)
    }
    _ => None
  }
}

///|
fn native_close(t : @live.LiveTransport) -> Bool raise @live.LiveTransportError {
  match t {
    NpcapLink(session, _, _) => {
      session.close() catch {
        err =>
          raise @live.LiveTransportError::IoFailed(
            "close",
            native_link_err_code(err),
            native_link_err_msg(err),
          )
      }
      true
    }
    RawSocketLink(session, _, _) => {
      session.close() catch {
        err =>
          raise @live.LiveTransportError::IoFailed(
            "close",
            native_link_err_code(err),
            native_link_err_msg(err),
          )
      }
      true
    }
    _ => false
  }
}

///|
fn native_local_mac(t : @live.LiveTransport) -> Bytes? {
  match t {
    NpcapLink(_, mac, _) => Some(mac)
    RawSocketLink(_, mac, _) => Some(mac)
    _ => None
  }
}

///|
fn native_live_opened(t : @live.LiveTransport) -> Bool? {
  match t {
    NpcapLink(_, _, opened) => Some(opened)
    RawSocketLink(_, _, opened) => Some(opened)
    _ => None
  }
}

///|
fn native_kind_label(kind : @live.LiveTransportKind) -> String? {
  match kind {
    NpcapKind => Some("npcap")
    RawSocketKind => Some("raw-socket")
    _ => None
  }
}

///|
fn native_now_millis() -> Int64? {
  Some(monotonic_millis())
}

///|
fn native_highres() -> Int64? {
  Some(highres_micros())
}

///|
fn native_sleep_until(deadline_micros : Int64) -> Int? {
  Some(sleep_until_micros(deadline_micros))
}

///|
fn native_rt_supported() -> Int? {
  Some(win_rt_supported())
}

///|
fn native_rt_affinity(mask : UInt, priority : Int) -> Int? {
  Some(win_rt_set_affinity(mask, priority))
}

///|
fn init {
  @live.register_live_transport_open(native_open)
  @live.register_live_transport_send(native_send)
  @live.register_live_transport_receive(native_receive)
  @live.register_live_transport_close(native_close)
  @live.register_live_transport_local_mac(native_local_mac)
  @live.register_live_transport_live_opened(native_live_opened)
  @live.register_live_transport_kind_label(native_kind_label)
  @live.register_live_now_millis(native_now_millis)
  @live.register_live_highres_micros(native_highres)
  @live.register_live_sleep_until(native_sleep_until)
  @live.register_live_win_rt_supported(native_rt_supported)
  @live.register_live_win_rt_set_affinity(native_rt_affinity)
}