#[macro_use]
extern crate tracing;
#[macro_use]
extern crate futures;


mod webbluetooth;
use js_sys;
use tokio_stream::StreamExt;
use crate::webbluetooth::{WebBluetoothCommunicationManagerBuilder};
use buttplug_core::{
  message::{ButtplugServerMessageCurrent, BUTTPLUG_CURRENT_API_MAJOR_VERSION, serializer::{ButtplugSerializedMessage, ButtplugMessageSerializer}},
  util::async_manager,
};
use buttplug_server::{
  ButtplugServerBuilder, ButtplugServer,
  device::ServerDeviceManagerBuilder,
  message::{ButtplugServerMessageVariant, serializer::ButtplugServerJSONSerializer},
};
use buttplug_server_device_config::{DeviceConfigurationManager, load_protocol_configs};

type FFICallback = js_sys::Function;
type FFICallbackContext = u32;

#[derive(Clone, Copy)]
#[allow(dead_code)]
pub struct FFICallbackContextWrapper(FFICallbackContext);

unsafe impl Send for FFICallbackContextWrapper {
}
unsafe impl Sync for FFICallbackContextWrapper {
}

use console_error_panic_hook;
use tracing_subscriber::{layer::SubscriberExt, Registry};
use tracing_wasm::{WASMLayer, WASMLayerConfig};
use wasm_bindgen::prelude::*;
use std::sync::Arc;
use js_sys::Uint8Array;

pub type ButtplugWASMServer = Arc<ButtplugServer>;

pub fn send_server_message(
  message: &ButtplugServerMessageCurrent,
  callback: &FFICallback,
) {
  let serializer = ButtplugServerJSONSerializer::default();
  serializer.force_message_version(&BUTTPLUG_CURRENT_API_MAJOR_VERSION);
  let json_msg = serializer.serialize(&[ButtplugServerMessageVariant::V4(message.clone())]);
  if let ButtplugSerializedMessage::Text(json) = json_msg {
    let buf = json.as_bytes();
    let this = JsValue::null();
    let uint8buf = unsafe { Uint8Array::new(&Uint8Array::view(buf)) };
    let _ = callback.call1(&this, &JsValue::from(uint8buf));
  }
}

#[no_mangle]
pub fn create_test_dcm(_allow_raw_messages: bool) -> DeviceConfigurationManager {
  load_protocol_configs(&None, &None, false)
    .expect("If this fails, the whole library goes with it.")
    .finish()
    .expect("If this fails, the whole library goes with it.")
}

#[no_mangle]
#[wasm_bindgen]
pub fn buttplug_create_embedded_wasm_server(
  callback: &FFICallback,
) -> *mut ButtplugWASMServer {
  console_error_panic_hook::set_once();
  let dcm = create_test_dcm(false);
  let mut sdm = ServerDeviceManagerBuilder::new(dcm);
  sdm.comm_manager(WebBluetoothCommunicationManagerBuilder::default());
  let builder = ButtplugServerBuilder::new(sdm.finish().unwrap());
  let server = Arc::new(builder.finish().unwrap());
  let event_stream = server.server_version_event_stream();
  let callback = callback.clone();
  async_manager::spawn(async move {
    pin_mut!(event_stream);
    while let Some(message) = event_stream.next().await {
      send_server_message(&message, &callback);
    }
  });

  Box::into_raw(Box::new(server))
}

#[no_mangle]
#[wasm_bindgen]
pub fn buttplug_free_embedded_wasm_server(ptr: *mut ButtplugWASMServer) {
  if !ptr.is_null() {
    unsafe {
      let _ = Box::from_raw(ptr);
    }
  }
}


#[no_mangle]
#[wasm_bindgen]
pub fn buttplug_client_send_json_message(
  server_ptr: *mut ButtplugWASMServer,
  buf: &[u8],
  callback: &FFICallback,
) {
  let server = unsafe {
    assert!(!server_ptr.is_null());
    &mut *server_ptr
  };
  let callback = callback.clone();
  let serializer = ButtplugServerJSONSerializer::default();
  serializer.force_message_version(&BUTTPLUG_CURRENT_API_MAJOR_VERSION);
  let input_msg = serializer.deserialize(&ButtplugSerializedMessage::Text(std::str::from_utf8(buf).unwrap().to_owned())).unwrap();
  async_manager::spawn(async move {
    let msg = input_msg[0].clone();
    let response = server.parse_message(msg).await.unwrap();
    let json_msg = serializer.serialize(&[response]);
    if let ButtplugSerializedMessage::Text(json) = json_msg {
      let buf = json.as_bytes();
      let this = JsValue::null();
      let uint8buf = unsafe { Uint8Array::new(&Uint8Array::view(buf)) };
      let _ = callback.call1(&this, &JsValue::from(uint8buf));
    }
  });
}

#[no_mangle]
#[wasm_bindgen]
pub fn buttplug_activate_env_logger(_max_level: &str) {
  tracing::subscriber::set_global_default(
    Registry::default()
      //.with(EnvFilter::new(max_level))
      .with(WASMLayer::new(WASMLayerConfig::default())),
  )
  .expect("default global");
}
