///|
/// Window state mapped from native window manager.
pub(all) enum WindowState {
  Unknown = 0
  Created = 1
  Running = 2
  Hidden = 3
  Closing = 4
  Closed = 5
} derive(Eq)

///|
pub fn WindowState::int(self : WindowState) -> Int = "%identity"

///|
test {
  inspect(WindowState::Created.int(), content="1")
}

///|
/// Process type returned by native window manager.
pub(all) enum ProcessType {
  Main = 0
  Child = 1
  Unknown = 2
} derive(Eq)

///|
/// IPC message kind.
pub(all) enum IpcMessageType {
  Data = 0
  Command = 1
  Event = 2
  Request = 3
  Response = 4
} derive(Eq)

///|
pub fn IpcMessageType::int(self : IpcMessageType) -> Int = "%identity"

///|
/// macOS title-bar style, mirrors Tauri 2's `titleBarStyle`. On other
/// platforms it selects the closest native title-bar behavior.
pub(all) enum TitleBarStyle {
  /// Visible: normal native title bar (default).
  Visible = 0
  /// Transparent: native title bar rendered transparent, still present.
  Transparent = 1
  /// Overlay: content extends underneath the native title bar (macOS keeps
  /// the traffic-light buttons); the typical starting point for a custom
  /// title bar.
  Overlay = 2
} derive(Debug, Eq)

///|
/// Native window style configuration, aligned with the subset of Tauri 2's
/// `WindowConfig` that maps onto a desktop window's native chrome.
///
/// Every field is optional: `None` means "not specified — don't touch that
/// native property", `Some(v)` means "apply `v`". This keeps the framework
/// from paying for native calls the caller never asked for; when *no* field
/// is set the FFI call is skipped altogether.
///
/// `decorations = Some(false)` is the frameless/borderless mode (`frameless`
/// in earlier Lepus versions).
pub struct WindowOptions {
  /// Whether the window keeps the OS-provided chrome (title bar + border).
  /// `Some(false)` produces a frameless window.
  decorations : Bool?
  /// Whether the user may resize the window.
  resizable : Bool?
  /// Whether the window's close button is enabled.
  closeable : Bool?
  /// Whether the window's minimize button is enabled.
  minimizable : Bool?
  /// Whether the window's maximize/zoom button is enabled.
  maximizable : Bool?
  /// Keep the window above all normal windows.
  always_on_top : Bool?
  /// Keep the window below all normal windows.
  always_on_bottom : Bool?
  /// Window background is transparent (requires page background support).
  transparent : Bool?
  /// Draw the system drop shadow.
  shadow : Bool?
  /// Hide the window from the taskbar / Dock window list.
  skip_taskbar : Bool?
  /// Show the window on every workspace (macOS/Linux).
  visible_on_all_workspaces : Bool?
  /// Native title-bar style (macOS `titleBarStyle`).
  title_bar_style : TitleBarStyle?
  /// Let window content extend under the title bar (custom title bars).
  title_bar_overlay : Bool?
  /// Hide only the title text while keeping the title bar.
  hidden_title : Bool?
}

///|
/// An empty patch: nothing is overridden, so the native window keeps its
/// platform defaults (which already match Tauri's documented defaults).
pub fn WindowOptions::default() -> WindowOptions {
  {
    decorations: None,
    resizable: None,
    closeable: None,
    minimizable: None,
    maximizable: None,
    always_on_top: None,
    always_on_bottom: None,
    transparent: None,
    shadow: None,
    skip_taskbar: None,
    visible_on_all_workspaces: None,
    title_bar_style: None,
    title_bar_overlay: None,
    hidden_title: None,
  }
}

///|
/// Returns `true` when the patch overrides nothing.
pub fn WindowOptions::is_empty(self : WindowOptions) -> Bool {
  self.wm_mask() == 0
}

///|
/// Returns `preferred` when it is set, otherwise `fallback`.
fn[T] wm_pick(preferred : T?, fallback : T?) -> T? {
  match preferred {
    Some(_) => preferred
    None => fallback
  }
}

///|
/// Merges `other` into `self`: a field set in `other` wins, an unset field
/// keeps `self`'s value. Lets callers apply incremental style patches without
/// forgetting the options they set earlier.
pub fn WindowOptions::merge(
  self : WindowOptions,
  other : WindowOptions,
) -> WindowOptions {
  {
    decorations: wm_pick(other.decorations, self.decorations),
    resizable: wm_pick(other.resizable, self.resizable),
    closeable: wm_pick(other.closeable, self.closeable),
    minimizable: wm_pick(other.minimizable, self.minimizable),
    maximizable: wm_pick(other.maximizable, self.maximizable),
    always_on_top: wm_pick(other.always_on_top, self.always_on_top),
    always_on_bottom: wm_pick(other.always_on_bottom, self.always_on_bottom),
    transparent: wm_pick(other.transparent, self.transparent),
    shadow: wm_pick(other.shadow, self.shadow),
    skip_taskbar: wm_pick(other.skip_taskbar, self.skip_taskbar),
    visible_on_all_workspaces: wm_pick(
      other.visible_on_all_workspaces,
      self.visible_on_all_workspaces,
    ),
    title_bar_style: wm_pick(other.title_bar_style, self.title_bar_style),
    title_bar_overlay: wm_pick(other.title_bar_overlay, self.title_bar_overlay),
    hidden_title: wm_pick(other.hidden_title, self.hidden_title),
  }
}

///|
test "merge applies only the fields set in the patch" {
  let base : WindowOptions = {
    ..WindowOptions::default(),
    decorations: Some(false),
    shadow: Some(false),
  }
  let patch : WindowOptions = {
    ..WindowOptions::default(),
    hidden_title: Some(true),
  }
  let merged = base.merge(patch)
  // Untouched by the patch: kept from `base`.
  assert_eq(merged.decorations, Some(false))
  assert_eq(merged.shadow, Some(false))
  // From the patch.
  assert_eq(merged.hidden_title, Some(true))
  // Never specified anywhere: still unset.
  assert_true(merged.transparent is None)
  assert_eq(
    merged.wm_mask(),
    WM_SET_DECORATIONS | WM_SET_SHADOW | WM_SET_HIDDEN_TITLE,
  )
}

///|
test "merge lets a later patch override an earlier value" {
  let base : WindowOptions = {
    ..WindowOptions::default(),
    shadow: Some(false),
  }
  let patch : WindowOptions = {
    ..WindowOptions::default(),
    shadow: Some(true),
  }
  assert_eq(base.merge(patch).shadow, Some(true))
  // An empty patch changes nothing.
  assert_eq(base.merge(WindowOptions::default()).shadow, Some(false))
}

///|
/// Encodes a boolean flag as the `1`/`0` int the native layer expects.
fn wm_flag(value : Bool) -> Int {
  if value {
    1
  } else {
    0
  }
}

///|
/// Encodes an optional boolean: unset flags are sent as `0` and are ignored
/// by the native side because their presence bit is clear.
fn wm_opt_flag(value : Bool?) -> Int {
  match value {
    Some(v) => wm_flag(v)
    None => 0
  }
}

///|
/// Presence bits for `wm_set_window_customization`. Must stay in sync with
/// the `WM_SET_*` macros in `ffi/window_manager/stub.c`.
pub const WM_SET_DECORATIONS : Int = 1 << 0

///|
pub const WM_SET_RESIZABLE : Int = 1 << 1

///|
pub const WM_SET_CLOSEABLE : Int = 1 << 2

///|
pub const WM_SET_MINIMIZABLE : Int = 1 << 3

///|
pub const WM_SET_MAXIMIZABLE : Int = 1 << 4

///|
pub const WM_SET_ALWAYS_ON_TOP : Int = 1 << 5

///|
pub const WM_SET_ALWAYS_ON_BOTTOM : Int = 1 << 6

///|
pub const WM_SET_TRANSPARENT : Int = 1 << 7

///|
pub const WM_SET_SHADOW : Int = 1 << 8

///|
pub const WM_SET_SKIP_TASKBAR : Int = 1 << 9

///|
pub const WM_SET_VISIBLE_ON_ALL_WORKSPACES : Int = 1 << 10

///|
pub const WM_SET_TITLE_BAR_STYLE : Int = 1 << 11

///|
pub const WM_SET_TITLE_BAR_OVERLAY : Int = 1 << 12

///|
pub const WM_SET_HIDDEN_TITLE : Int = 1 << 13

///|
/// Bit for `flag`, or `0` when the option was not specified.
fn wm_presence(bit : Int, specified : Bool) -> Int {
  if specified {
    bit
  } else {
    0
  }
}

///|
/// Builds the presence mask: one bit per field that is actually set.
pub fn WindowOptions::wm_mask(self : WindowOptions) -> Int {
  wm_presence(WM_SET_DECORATIONS, self.decorations is Some(_)) |
  wm_presence(WM_SET_RESIZABLE, self.resizable is Some(_)) |
  wm_presence(WM_SET_CLOSEABLE, self.closeable is Some(_)) |
  wm_presence(WM_SET_MINIMIZABLE, self.minimizable is Some(_)) |
  wm_presence(WM_SET_MAXIMIZABLE, self.maximizable is Some(_)) |
  wm_presence(WM_SET_ALWAYS_ON_TOP, self.always_on_top is Some(_)) |
  wm_presence(WM_SET_ALWAYS_ON_BOTTOM, self.always_on_bottom is Some(_)) |
  wm_presence(WM_SET_TRANSPARENT, self.transparent is Some(_)) |
  wm_presence(WM_SET_SHADOW, self.shadow is Some(_)) |
  wm_presence(WM_SET_SKIP_TASKBAR, self.skip_taskbar is Some(_)) |
  wm_presence(
    WM_SET_VISIBLE_ON_ALL_WORKSPACES,
    self.visible_on_all_workspaces is Some(_),
  ) |
  wm_presence(WM_SET_TITLE_BAR_STYLE, self.title_bar_style is Some(_)) |
  wm_presence(WM_SET_TITLE_BAR_OVERLAY, self.title_bar_overlay is Some(_)) |
  wm_presence(WM_SET_HIDDEN_TITLE, self.hidden_title is Some(_))
}

///|
/// `true` when the patch explicitly asks for a custom (non-native) title bar,
/// which needs the injected title-bar CSS/JS support layer.
pub fn WindowOptions::wants_custom_titlebar(self : WindowOptions) -> Bool {
  if self.decorations is Some(false) {
    return true
  }
  if self.title_bar_overlay is Some(true) {
    return true
  }
  match self.title_bar_style {
    Some(Transparent) | Some(Overlay) => true
    _ => false
  }
}

///|
/// `true` only when the patch asks for a transparent background. An unset
/// `transparent` must stay `false`: injecting the transparency support layer
/// is expensive and would change the look of windows that never asked.
pub fn WindowOptions::wants_transparency(self : WindowOptions) -> Bool {
  self.transparent is Some(true)
}

///|
test "WindowOptions::default overrides nothing" {
  let options = WindowOptions::default()
  assert_eq(options.wm_mask(), 0)
  assert_true(options.is_empty())
}

///|
test "wm_mask sets exactly one bit per specified option" {
  let options : WindowOptions = {
    ..WindowOptions::default(),
    shadow: Some(false),
  }
  assert_eq(options.wm_mask(), WM_SET_SHADOW)
  assert_false(options.is_empty())
  let both : WindowOptions = {
    ..WindowOptions::default(),
    decorations: Some(false),
    title_bar_style: Some(Overlay),
  }
  assert_eq(both.wm_mask(), WM_SET_DECORATIONS | WM_SET_TITLE_BAR_STYLE)
}

///|
test "wants_custom_titlebar and wants_transparency require an explicit opt-in" {
  assert_false(WindowOptions::default().wants_custom_titlebar())
  assert_false(WindowOptions::default().wants_transparency())
  let frameless : WindowOptions = {
    ..WindowOptions::default(),
    decorations: Some(false),
  }
  assert_true(frameless.wants_custom_titlebar())
  let overlay : WindowOptions = {
    ..WindowOptions::default(),
    title_bar_style: Some(Overlay),
  }
  assert_true(overlay.wants_custom_titlebar())
  let opaque_patch : WindowOptions = {
    ..WindowOptions::default(),
    transparent: Some(false),
  }
  assert_false(opaque_patch.wants_transparency())
}

///|
test "wm_opt_flag encodes unset options as 0 and pins the flag layout" {
  assert_eq(wm_opt_flag(Some(true)), 1)
  assert_eq(wm_opt_flag(Some(false)), 0)
  assert_eq(wm_opt_flag(None), 0)
}

///|
test "wm_presence yields the bit only for specified options" {
  assert_eq(wm_presence(WM_SET_SHADOW, true), WM_SET_SHADOW)
  assert_eq(wm_presence(WM_SET_SHADOW, false), 0)
}

///|
/// Thin high-level wrapper over native window manager (stub.c wm_* APIs).
pub struct WindowManager {
  process_type : ProcessType
  mut window_ids : Array[Int]
}

///|
/// Snapshot of one IPC message copied out of the native queue.
pub struct IpcMessage {
  source_window_id : Int
  target_window_id : Int
  message_type : IpcMessageType
  message_id : Int
  subtype : String
  data : String
}

///|
fn i32_le_at(wire : Bytes, offset : Int) -> Int? {
  guard offset >= 0 && offset + 3 < wire.length() else { None }
  let b0 = wire[offset].to_int()
  let b1 = wire[offset + 1].to_int()
  let b2 = wire[offset + 2].to_int()
  let b3 = wire[offset + 3].to_int()
  Some(b0 + (b1 << 8) + (b2 << 16) + (b3 << 24))
}

///|
fn ipc_message_type_from_int(value : Int) -> IpcMessageType {
  match value {
    0 => IpcMessageType::Data
    1 => Command
    2 => Event
    3 => Request
    4 => Response
    _ => Data
  }
}

///|
fn decode_ipc_message_wire(wire : Bytes) -> IpcMessage? {
  guard wire.length() >= 24 else { None }
  let source_window_id = i32_le_at(wire, 0).unwrap()
  let target_window_id = i32_le_at(wire, 4).unwrap()
  let message_type = ipc_message_type_from_int(i32_le_at(wire, 8).unwrap())
  let message_id = i32_le_at(wire, 12).unwrap()
  let subtype_len = i32_le_at(wire, 16).unwrap()
  let data_len = i32_le_at(wire, 20).unwrap()
  guard subtype_len >= 0 && data_len >= 0 else { None }
  let subtype_start = 24
  let data_start = subtype_start + subtype_len
  guard data_start + data_len <= wire.length() else { None }
  let subtype = @encoding/utf8.decode(
    wire.exact_view(start=subtype_start, end=data_start),
  ) catch {
    _ => ""
  }
  let data = @encoding/utf8.decode(
    wire.exact_view(start=data_start, end=data_start + data_len),
  ) catch {
    _ => ""
  }
  Some({
    source_window_id,
    target_window_id,
    message_type,
    message_id,
    subtype,
    data,
  })
}

///|
fn i32_le_bytes(value : Int) -> Bytes {
  Bytes::makei(4, i => {
    match i {
      0 => (value & 0xFF).to_byte()
      1 => ((value >> 8) & 0xFF).to_byte()
      2 => ((value >> 16) & 0xFF).to_byte()
      _ => ((value >> 24) & 0xFF).to_byte()
    }
  })
}

///|
test "decode_ipc_message_wire" {
  let subtype = @encoding/utf8.encode("ready")
  let data = @encoding/utf8.encode("{\"ok\":true}")
  let wire = i32_le_bytes(7) +
    i32_le_bytes(9) +
    i32_le_bytes(IpcMessageType::Request.int()) +
    i32_le_bytes(42) +
    i32_le_bytes(subtype.length()) +
    i32_le_bytes(data.length()) +
    subtype +
    data
  let message = decode_ipc_message_wire(wire).unwrap()
  inspect(message.source_window_id, content="7")
  inspect(message.target_window_id, content="9")
  inspect(message.message_type == Request, content="true")
  inspect(message.message_id, content="42")
  inspect(message.subtype, content="ready")
  inspect(message.data, content="{\"ok\":true}")
  inspect(decode_ipc_message_wire(b"") is None, content="true")
  inspect(decode_ipc_message_wire(b"\x01\x02") is None, content="true")
}

///|
/// Initialize WM runtime for current process.
///
/// - main process: pass `true`
/// - child process: pass `false`
pub fn WindowManager::init(is_main? : Bool = true) -> WindowManager {
  let ok = @window_manager.wm_init(if is_main { 1 } else { 0 })
  guard ok == 0 else { abort("WindowManager::init failed") }
  let process_type = match @window_manager.wm_get_process_type() {
    0 => ProcessType::Main
    1 => Child
    _ => Unknown
  }
  { process_type, window_ids: [], }
}

///|
/// Install the standard application main menu (macOS) so keyboard shortcuts
/// work: Cmd+Q (quit app), Cmd+W (close window), Cmd+M (minimize), and the
/// Edit menu (Cmd+Z/X/C/V/A). No-op on other platforms.
pub fn WindowManager::install_default_app_menu(_self : WindowManager) -> Int {
  @window_manager.wm_install_default_app_menu()
}

///|
/// Set the NSApplication activation policy (macOS). No-op on other platforms.
pub fn WindowManager::set_activation_policy(
  _self : WindowManager,
  policy : Int,
) -> Int {
  @window_manager.wm_set_activation_policy(policy)
}

///|
/// Cleanup native WM runtime.
pub fn WindowManager::destroy(self : WindowManager) -> Unit {
  @window_manager.wm_cleanup()
  self.window_ids = []
}

///|
/// Create a native window in current process.
pub fn WindowManager::create_window(
  self : WindowManager,
  title : String,
  url : String,
  width? : Int = 800,
  height? : Int = 600,
) -> Int {
  let id = @window_manager.wm_create_window(
    @encoding/utf8.encode(title),
    @encoding/utf8.encode(url),
    width,
    height,
    -1,
    -1,
    0,
    0,
    -1,
  )
  guard id > 0 else { abort("WindowManager::create_window failed") }
  self.window_ids.push(id)
  id
}

///|
/// Spawn a child process window. Returns child process pid.
pub fn WindowManager::create_child_window(
  _self : WindowManager,
  title : String,
  url : String,
  width? : Int = 800,
  height? : Int = 600,
  parent_id? : Int = -1,
) -> Int {
  @window_manager.wm_create_child_window(
    @encoding/utf8.encode(title),
    @encoding/utf8.encode(url),
    width,
    height,
    parent_id,
  )
}

///|
/// Fork the current process. The parent receives the child pid, the child receives `0`.
pub fn WindowManager::fork_process(_self : WindowManager) -> Int {
  @window_manager.wm_fork_process()
}

///|
/// Spawn a fresh child process by exec-ing the given program path with one extra argument.
pub fn WindowManager::spawn_process(
  _self : WindowManager,
  program : String,
  arg1 : String,
) -> Int {
  @window_manager.wm_spawn_process(
    @encoding/utf8.encode(program),
    @encoding/utf8.encode(arg1),
  )
}

///|
/// Connect current process to the main-process IPC server and switch into child mode.
pub fn WindowManager::connect_child_process() -> Int {
  @window_manager.wm_connect_child_process()
}

///|
/// Set the current (child) process window-id starting base.
///
/// The main process assigns each child a globally unique base so that windows
/// created in different children never collide on `window_id` (used for IPC
/// routing). Call before creating any window in the child process.
pub fn WindowManager::set_window_id_base(
  _self : WindowManager,
  base : Int,
) -> Int {
  @window_manager.wm_set_window_id_base(base)
}

///|
/// Return true when running in main process.
pub fn WindowManager::is_main_process(self : WindowManager) -> Bool {
  self.process_type == Main
}

///|
/// Return true when running in child process.
pub fn WindowManager::is_child_process(self : WindowManager) -> Bool {
  self.process_type == Child
}

///|
/// Run a window event loop.
pub fn WindowManager::run_window(_self : WindowManager, window_id : Int) -> Int {
  @window_manager.wm_run_window(window_id)
}

///|
/// Destroy a window.
pub fn WindowManager::destroy_window(
  _self : WindowManager,
  window_id : Int,
) -> Int {
  @window_manager.wm_destroy_window(window_id)
}

///|
/// Flatten `options` into the positional FFI call.
///
/// Returns `0` without calling into C when the patch is empty, so windows that
/// only use platform defaults never pay for a native round-trip.
fn wm_apply_window_options(window_id : Int, options : WindowOptions) -> Int {
  let mask = options.wm_mask()
  if mask == 0 {
    return 0
  }
  wm_set_window_customization(
    window_id,
    mask,
    wm_opt_flag(options.decorations),
    wm_opt_flag(options.resizable),
    wm_opt_flag(options.closeable),
    wm_opt_flag(options.minimizable),
    wm_opt_flag(options.maximizable),
    wm_opt_flag(options.always_on_top),
    wm_opt_flag(options.always_on_bottom),
    wm_opt_flag(options.transparent),
    wm_opt_flag(options.shadow),
    wm_opt_flag(options.skip_taskbar),
    wm_opt_flag(options.visible_on_all_workspaces),
    options.title_bar_style.unwrap_or(Visible),
    wm_opt_flag(options.title_bar_overlay),
    wm_opt_flag(options.hidden_title),
  )
}

///|
/// Apply the native window style described by `options`.
pub fn WindowManager::set_window_customization(
  _self : WindowManager,
  window_id : Int,
  options : WindowOptions,
) -> Int {
  wm_apply_window_options(window_id, options)
}

///|
/// Set macOS traffic-light buttons position.
pub fn WindowManager::set_traffic_light_position(
  _self : WindowManager,
  window_id : Int,
  x : Int,
  y : Int,
) -> Int {
  @window_manager.wm_set_traffic_light_position(window_id, x, y)
}

///|
/// Move native window to an absolute screen position.
pub fn WindowManager::set_position(
  _self : WindowManager,
  window_id : Int,
  x : Int,
  y : Int,
) -> Int {
  @window_manager.wm_set_position(window_id, x, y)
}

///|
/// Resize native window.
pub fn WindowManager::set_size(
  _self : WindowManager,
  window_id : Int,
  width : Int,
  height : Int,
  hints : SizeHint,
) -> Int {
  wm_set_size(window_id, width, height, hints)
}

///|
/// Minimize native window.
pub fn WindowManager::minimize_window(
  _self : WindowManager,
  window_id : Int,
) -> Int {
  @window_manager.wm_minimize_window(window_id)
}

///|
/// Maximize native window.
pub fn WindowManager::maximize_window(
  _self : WindowManager,
  window_id : Int,
) -> Int {
  @window_manager.wm_maximize_window(window_id)
}

///|
/// Restore native window from maximized state.
pub fn WindowManager::unmaximize_window(
  _self : WindowManager,
  window_id : Int,
) -> Int {
  @window_manager.wm_unmaximize_window(window_id)
}

///|
/// Toggle native maximize state.
pub fn WindowManager::toggle_maximize_window(
  _self : WindowManager,
  window_id : Int,
) -> Int {
  @window_manager.wm_toggle_maximize_window(window_id)
}

///|
/// Set native fullscreen state.
pub fn WindowManager::set_fullscreen_window(
  _self : WindowManager,
  window_id : Int,
  fullscreen : Bool,
) -> Int {
  @window_manager.wm_set_fullscreen_window(
    window_id,
    if fullscreen {
      1
    } else {
      0
    },
  )
}

///|
/// Toggle native fullscreen state.
pub fn WindowManager::toggle_fullscreen_window(
  _self : WindowManager,
  window_id : Int,
) -> Int {
  @window_manager.wm_toggle_fullscreen_window(window_id)
}

///|
/// Start native window drag/move gesture.
pub fn WindowManager::start_drag_window(
  _self : WindowManager,
  window_id : Int,
) -> Int {
  @window_manager.wm_start_drag_window(window_id)
}

///|
/// Show or hide a native window.
pub fn WindowManager::set_visibility(
  _self : WindowManager,
  window_id : Int,
  visible : Bool,
) -> Int {
  @window_manager.wm_set_visibility(window_id, if visible { 1 } else { 0 })
}

///|
/// Close native window.
pub fn WindowManager::close_window(
  _self : WindowManager,
  window_id : Int,
) -> Int {
  @window_manager.wm_close_window(window_id)
}

///|
/// Bring a window to front and focus it. On macOS this also activates the
/// process as the foreground application (Tauri `Window::set_focus` parity).
pub fn WindowManager::focus_window(
  _self : WindowManager,
  window_id : Int,
) -> Int {
  @window_manager.wm_focus_window(window_id)
}

///|
/// Enable or disable developer tools integration.
pub fn WindowManager::set_devtools(
  _self : WindowManager,
  window_id : Int,
  enabled : Bool,
) -> Int {
  @window_manager.wm_set_devtools(window_id, if enabled { 1 } else { 0 })
}

///|
/// Send an IPC message.
pub fn WindowManager::send_message(
  _self : WindowManager,
  source_window_id : Int,
  target_window_id : Int,
  message_type : IpcMessageType,
  subtype : String,
  data : String,
) -> Int {
  @window_manager.wm_ipc_send(
    source_window_id,
    target_window_id,
    message_type.int(),
    @encoding/utf8.encode(subtype),
    @encoding/utf8.encode(data),
  )
}

///|
/// Broadcast an event message.
pub fn WindowManager::broadcast(
  self : WindowManager,
  source_window_id : Int,
  subtype : String,
  data : String,
) -> Int {
  self.send_message(source_window_id, 0, Event, subtype, data)
}

///|
/// Send an IPC request and wait for the response body.
pub fn WindowManager::request(
  _self : WindowManager,
  source_window_id : Int,
  target_window_id : Int,
  subtype : String,
  data : String,
  timeout_ms? : Int = 0,
) -> String {
  @encoding/utf8.decode(
    @window_manager.wm_ipc_request_bytes(
      source_window_id,
      target_window_id,
      @encoding/utf8.encode(subtype),
      @encoding/utf8.encode(data),
      timeout_ms,
    ),
  ) catch {
    _ => ""
  }
}

///|
/// Send an IPC response for a previously received request.
pub fn WindowManager::respond(
  _self : WindowManager,
  source_window_id : Int,
  target_window_id : Int,
  request_id : Int,
  data : String,
) -> Int {
  @window_manager.wm_ipc_respond(
    source_window_id,
    target_window_id,
    request_id,
    @encoding/utf8.encode(data),
  )
}

///|
/// Try to pop one queued IPC message for the given queue id.
pub fn WindowManager::try_pop_message(
  _self : WindowManager,
  queue_id : Int,
) -> IpcMessage? {
  decode_ipc_message_wire(@window_manager.wm_ipc_pop_message_wire(queue_id))
}

///|
/// Non-blocking child process status check.
pub fn WindowManager::wait_child_noblock(
  _self : WindowManager,
  pid : Int,
) -> Int {
  @window_manager.wm_wait_child_noblock(pid)
}