///|
/// The native backend is selected by the target operating system at compile time.
priv enum NativeBackend {
  Windows
  MacOS
  Linux
  Ohos
  Unsupported
} derive(Eq)

///|
fn active_backend() -> NativeBackend {
  if @windows.available() != 0 {
    NativeBackend::Windows
  } else if @macos.available() != 0 {
    NativeBackend::MacOS
  } else if @linux.available() != 0 {
    NativeBackend::Linux
  } else if @ohos.available() != 0 {
    NativeBackend::Ohos
  } else {
    NativeBackend::Unsupported
  }
}

///|
fn native_available() -> Bool {
  active_backend() != NativeBackend::Unsupported
}

///|
fn native_thread_token() -> UInt64 {
  match active_backend() {
    NativeBackend::Windows => @windows.current_thread_token()
    NativeBackend::MacOS => @macos.current_thread_token()
    NativeBackend::Linux => @linux.current_thread_token()
    _ => 0UL
  }
}

///|
fn native_is_owner_thread(owner_thread : UInt64) -> Bool {
  owner_thread != 0UL && native_thread_token() == owner_thread
}

///|
fn native_is_ohos() -> Bool {
  active_backend() == NativeBackend::Ohos
}

///|
fn install_native_callbacks(
  event_callback : (UInt64, Int, Bytes, Bytes, Int) -> Unit,
  navigation_callback : (UInt64, Bytes) -> Int,
  new_window_callback : (UInt64, Bytes) -> Int,
  protocol_callback : (UInt64, Bytes, Bytes, Bytes, Bytes, Bytes, Bytes) -> Unit,
  media_permission_callback : (UInt64, Int, Bytes) -> Int,
) -> Unit {
  match active_backend() {
    NativeBackend::Windows => {
      @windows.install_event_callback(
        fn(callback, handle, kind, value, detail, code) {
          callback(handle, kind, value, detail, code)
        },
        event_callback,
      )
      @windows.install_navigation_callback(
        fn(callback, handle, url) { callback(handle, url) },
        navigation_callback,
      )
      @windows.install_new_window_callback(
        fn(callback, handle, url) { callback(handle, url) },
        new_window_callback,
      )
      @windows.install_protocol_callback(
        fn(
          callback,
          handle,
          request_id,
          scheme,
          http_method,
          uri,
          headers,
          body,
        ) {
          callback(handle, request_id, scheme, http_method, uri, headers, body)
        },
        protocol_callback,
      )
      @windows.install_media_permission_callback(
        fn(callback, handle, kind, origin) { callback(handle, kind, origin) },
        media_permission_callback,
      )
    }
    NativeBackend::MacOS => {
      @macos.install_event_callback(
        fn(callback, handle, kind, value, detail, code) {
          callback(handle, kind, value, detail, code)
        },
        event_callback,
      )
      @macos.install_navigation_callback(
        fn(callback, handle, url) { callback(handle, url) },
        navigation_callback,
      )
      @macos.install_new_window_callback(
        fn(callback, handle, url) { callback(handle, url) },
        new_window_callback,
      )
      @macos.install_protocol_callback(
        fn(
          callback,
          handle,
          request_id,
          scheme,
          http_method,
          uri,
          headers,
          body,
        ) {
          callback(handle, request_id, scheme, http_method, uri, headers, body)
        },
        protocol_callback,
      )
      @macos.install_media_permission_callback(
        fn(callback, handle, kind, origin) { callback(handle, kind, origin) },
        media_permission_callback,
      )
    }
    NativeBackend::Linux => {
      @linux.install_event_callback(
        fn(callback, handle, kind, value, detail, code) {
          callback(handle, kind, value, detail, code)
        },
        event_callback,
      )
      @linux.install_navigation_callback(
        fn(callback, handle, url) { callback(handle, url) },
        navigation_callback,
      )
      @linux.install_new_window_callback(
        fn(callback, handle, url) { callback(handle, url) },
        new_window_callback,
      )
      @linux.install_protocol_callback(
        fn(
          callback,
          handle,
          request_id,
          scheme,
          http_method,
          uri,
          headers,
          body,
        ) {
          callback(handle, request_id, scheme, http_method, uri, headers, body)
        },
        protocol_callback,
      )
      @linux.install_media_permission_callback(
        fn(callback, handle, kind, origin) { callback(handle, kind, origin) },
        media_permission_callback,
      )
    }
    NativeBackend::Ohos => {
      @ohos.install_event_callback(
        fn(callback, handle, kind, value, detail, code) {
          callback(handle, kind, value, detail, code)
        },
        event_callback,
      )
      @ohos.install_navigation_callback(
        fn(callback, handle, url) { callback(handle, url) },
        navigation_callback,
      )
      @ohos.install_protocol_callback(
        fn(
          callback,
          handle,
          request_id,
          scheme,
          http_method,
          uri,
          headers,
          body,
        ) {
          callback(handle, request_id, scheme, http_method, uri, headers, body)
        },
        protocol_callback,
      )
      @ohos.install_media_permission_callback(
        fn(callback, handle, kind, origin) { callback(handle, kind, origin) },
        media_permission_callback,
      )
    }
    NativeBackend::Unsupported => ()
  }
}

///|
fn native_register_custom_scheme(name : Bytes) -> Int {
  match active_backend() {
    NativeBackend::Windows => @windows.register_custom_scheme(name)
    NativeBackend::MacOS => @macos.register_custom_scheme(name)
    NativeBackend::Linux => @linux.register_custom_scheme(name)
    NativeBackend::Ohos => 0
    NativeBackend::Unsupported => 0
  }
}

///|
fn native_lock_custom_schemes() -> Unit {
  match active_backend() {
    NativeBackend::Windows => @windows.lock_custom_schemes()
    NativeBackend::MacOS => @macos.lock_custom_schemes()
    NativeBackend::Linux => @linux.lock_custom_schemes()
    NativeBackend::Ohos => ()
    NativeBackend::Unsupported => ()
  }
}

///|
fn native_respond_protocol(
  handle : UInt64,
  request_id : Bytes,
  status : Int,
  headers : Bytes,
  body : Bytes,
) -> Int {
  match active_backend() {
    NativeBackend::Windows =>
      @windows.respond_protocol(handle, request_id, status, headers, body)
    NativeBackend::MacOS =>
      @macos.respond_protocol(handle, request_id, status, headers, body)
    NativeBackend::Linux =>
      @linux.respond_protocol(handle, request_id, status, headers, body)
    NativeBackend::Ohos => 0
    NativeBackend::Unsupported => 0
  }
}

///|
fn native_create(
  context_id : UInt64,
  ephemeral : Bool,
  data_directory : Bytes,
  parent_handle : UInt64,
  x : Int,
  y : Int,
  width : Int,
  height : Int,
  initial_url : Bytes,
  initial_html : Bytes,
  initialization_script : Bytes,
  user_agent : Bytes,
  max_pending_commands : Int,
  max_pending_command_bytes : Int,
  max_protocol_request_body_bytes : Int,
) -> UInt64 {
  match active_backend() {
    NativeBackend::Windows =>
      @windows.create(
        context_id, ephemeral, data_directory, parent_handle, x, y, width, height,
        initial_url, initial_html, initialization_script, user_agent, max_pending_commands,
        max_pending_command_bytes, max_protocol_request_body_bytes,
      )
    NativeBackend::MacOS =>
      @macos.create(
        context_id, ephemeral, data_directory, parent_handle, x, y, width, height,
        initial_url, initial_html, initialization_script, user_agent, max_pending_commands,
        max_pending_command_bytes, max_protocol_request_body_bytes,
      )
    NativeBackend::Linux =>
      @linux.create(
        context_id, ephemeral, data_directory, parent_handle, x, y, width, height,
        initial_url, initial_html, initialization_script, user_agent, max_pending_commands,
        max_pending_command_bytes, max_protocol_request_body_bytes,
      )
    NativeBackend::Ohos => 0
    NativeBackend::Unsupported => 0
  }
}

///|
fn native_start(handle : UInt64) -> Unit {
  match active_backend() {
    NativeBackend::Windows => @windows.start(handle)
    NativeBackend::MacOS => @macos.start(handle)
    NativeBackend::Linux => @linux.start(handle)
    NativeBackend::Ohos => ()
    NativeBackend::Unsupported => ()
  }
}

///|
fn native_destroy(handle : UInt64) -> Int {
  match active_backend() {
    NativeBackend::Windows => @windows.destroy(handle)
    NativeBackend::MacOS => @macos.destroy(handle)
    NativeBackend::Linux => @linux.destroy(handle)
    NativeBackend::Ohos => @ohos.destroy(handle)
    NativeBackend::Unsupported => 0
  }
}

///|
fn native_set_bounds(
  handle : UInt64,
  x : Int,
  y : Int,
  width : Int,
  height : Int,
) -> Int {
  match active_backend() {
    NativeBackend::Windows => @windows.set_bounds(handle, x, y, width, height)
    NativeBackend::MacOS => @macos.set_bounds(handle, x, y, width, height)
    NativeBackend::Linux => @linux.set_bounds(handle, x, y, width, height)
    NativeBackend::Ohos => 0
    NativeBackend::Unsupported => 0
  }
}

///|
fn native_set_visible(handle : UInt64, visible : Bool) -> Int {
  let visible = if visible { 1 } else { 0 }
  match active_backend() {
    NativeBackend::Windows => @windows.set_visible(handle, visible)
    NativeBackend::MacOS => @macos.set_visible(handle, visible)
    NativeBackend::Linux => @linux.set_visible(handle, visible)
    NativeBackend::Ohos => 0
    NativeBackend::Unsupported => 0
  }
}

///|
fn native_focus(handle : UInt64) -> Int {
  match active_backend() {
    NativeBackend::Windows => @windows.focus(handle)
    NativeBackend::MacOS => @macos.focus(handle)
    NativeBackend::Linux => @linux.focus(handle)
    NativeBackend::Ohos => 0
    NativeBackend::Unsupported => 0
  }
}

///|
fn native_navigate(handle : UInt64, url : Bytes) -> Int {
  match active_backend() {
    NativeBackend::Windows => @windows.navigate(handle, url)
    NativeBackend::MacOS => @macos.navigate(handle, url)
    NativeBackend::Linux => @linux.navigate(handle, url)
    NativeBackend::Ohos => 0
    NativeBackend::Unsupported => 0
  }
}

///|
fn native_load_html(handle : UInt64, html : Bytes) -> Int {
  match active_backend() {
    NativeBackend::Windows => @windows.load_html(handle, html)
    NativeBackend::MacOS => @macos.load_html(handle, html)
    NativeBackend::Linux => @linux.load_html(handle, html)
    NativeBackend::Ohos => 0
    NativeBackend::Unsupported => 0
  }
}

///|
fn native_reload(handle : UInt64) -> Int {
  match active_backend() {
    NativeBackend::Windows => @windows.reload(handle)
    NativeBackend::MacOS => @macos.reload(handle)
    NativeBackend::Linux => @linux.reload(handle)
    NativeBackend::Ohos => @ohos.reload(handle)
    NativeBackend::Unsupported => 0
  }
}

///|
fn native_stop(handle : UInt64) -> Int {
  match active_backend() {
    NativeBackend::Windows => @windows.stop(handle)
    NativeBackend::MacOS => @macos.stop(handle)
    NativeBackend::Linux => @linux.stop(handle)
    NativeBackend::Ohos => 0
    NativeBackend::Unsupported => 0
  }
}

///|
fn native_go_back(handle : UInt64) -> Int {
  match active_backend() {
    NativeBackend::Windows => @windows.go_back(handle)
    NativeBackend::MacOS => @macos.go_back(handle)
    NativeBackend::Linux => @linux.go_back(handle)
    NativeBackend::Ohos => 0
    NativeBackend::Unsupported => 0
  }
}

///|
fn native_go_forward(handle : UInt64) -> Int {
  match active_backend() {
    NativeBackend::Windows => @windows.go_forward(handle)
    NativeBackend::MacOS => @macos.go_forward(handle)
    NativeBackend::Linux => @linux.go_forward(handle)
    NativeBackend::Ohos => 0
    NativeBackend::Unsupported => 0
  }
}

///|
fn native_add_init_script(handle : UInt64, script : Bytes) -> Int {
  match active_backend() {
    NativeBackend::Windows => @windows.add_init_script(handle, script)
    NativeBackend::MacOS => @macos.add_init_script(handle, script)
    NativeBackend::Linux => @linux.add_init_script(handle, script)
    NativeBackend::Ohos => 0
    NativeBackend::Unsupported => 0
  }
}

///|
fn native_set_zoom(handle : UInt64, factor : Double) -> Int {
  match active_backend() {
    NativeBackend::Windows => @windows.set_zoom(handle, factor)
    NativeBackend::MacOS => @macos.set_zoom(handle, factor)
    NativeBackend::Linux => @linux.set_zoom(handle, factor)
    NativeBackend::Ohos => 0
    NativeBackend::Unsupported => 0
  }
}

///|
fn native_open_devtools(handle : UInt64) -> Int {
  match active_backend() {
    NativeBackend::Windows => @windows.open_devtools(handle)
    NativeBackend::MacOS => @macos.open_devtools(handle)
    NativeBackend::Linux => @linux.open_devtools(handle)
    NativeBackend::Ohos => 0
    NativeBackend::Unsupported => 0
  }
}

///|
fn native_open_print_dialog(handle : UInt64) -> Int {
  match active_backend() {
    NativeBackend::Windows => @windows.open_print_dialog(handle)
    NativeBackend::MacOS => @macos.open_print_dialog(handle)
    NativeBackend::Linux => @linux.open_print_dialog(handle)
    NativeBackend::Ohos => 0
    NativeBackend::Unsupported => 0
  }
}

///|
fn native_show_file_dialog(
  handle : UInt64,
  kind : Int,
  title : Bytes,
  filters : Bytes,
  default_name : Bytes,
  initial_directory : Bytes,
  status : Ref[Int],
) -> Bytes {
  match active_backend() {
    NativeBackend::Windows =>
      @windows.show_file_dialog(
        handle, kind, title, filters, default_name, initial_directory, status,
      )
    NativeBackend::MacOS =>
      @macos.show_file_dialog(
        handle, kind, title, filters, default_name, initial_directory, status,
      )
    NativeBackend::Linux =>
      @linux.show_file_dialog(
        handle, kind, title, filters, default_name, initial_directory, status,
      )
    NativeBackend::Ohos | NativeBackend::Unsupported => {
      status.val = -1
      b""
    }
  }
}

///|
fn native_eval(handle : UInt64, script : Bytes, request_id : Bytes) -> Int {
  match active_backend() {
    NativeBackend::Windows => @windows.eval(handle, script, request_id)
    NativeBackend::MacOS => @macos.eval(handle, script, request_id)
    NativeBackend::Linux => @linux.eval(handle, script, request_id)
    NativeBackend::Ohos => @ohos.eval(handle, script)
    NativeBackend::Unsupported => 0
  }
}

///|
fn native_post_message(handle : UInt64, message : Bytes) -> Int {
  match active_backend() {
    NativeBackend::Windows => @windows.post_message(handle, message)
    NativeBackend::MacOS => @macos.post_message(handle, message)
    NativeBackend::Linux => @linux.post_message(handle, message)
    NativeBackend::Ohos => 0
    NativeBackend::Unsupported => 0
  }
}

///|
fn native_attach_ohos(web_tag : Bytes) -> UInt64 {
  match active_backend() {
    NativeBackend::Ohos => @ohos.attach(web_tag)
    _ => 0
  }
}

///|
fn native_start_ohos(handle : UInt64) -> Unit {
  match active_backend() {
    NativeBackend::Ohos => @ohos.start(handle)
    _ => ()
  }
}