#![deny(missing_docs)]
#![deny(clippy::missing_docs_in_private_items)]
#![warn(unused_extern_crates)]

//! This library is intended to eventually be a cross-platform bluetooth handling platform
//! Android portions adapted from <https://github.com/wuwbobo2021/android-bluetooth-serial-rs>

#[cfg(target_os = "android")]
use std::sync::Arc;
#[cfg(target_os = "android")]
use std::sync::Mutex;
#[cfg(target_os = "android")]
mod android;
#[cfg(target_os = "android")]
pub use android::Bluetooth;
#[cfg(target_os = "android")]
pub use android::Java;
#[cfg(target_os = "android")]
use winit::platform::android::activity::AndroidApp;

#[cfg(target_os = "linux")]
mod linux;

#[cfg(target_os = "windows")]
mod windows;

mod bluetooth_uuid;
pub use bluetooth_uuid::BluetoothUuid;

mod sdp;

/// Commands issued to the library
#[derive(Debug, serde::Deserialize, serde::Serialize)]
pub enum BluetoothCommand {
    /// Detect all bluetooth adapters present on the system
    DetectAdapters,
    /// Find out how many bluetooth adapters are detected
    QueryNumAdapters,
}

/// Messages that can be sent specifically to the app user hosting the bluetooth controls
pub enum MessageToBluetoothHost {
    /// The passkey used for pairing devices
    DisplayPasskey(u32, tokio::sync::mpsc::Sender<ResponseToPasskey>),
    /// The passkey to confirm for pairing
    ConfirmPasskey(u32, tokio::sync::mpsc::Sender<ResponseToPasskey>),
    /// Cancal the passkey display
    CancelDisplayPasskey,
}

#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
/// Messages that are send directly from the bluetooth host
pub enum MessageFromBluetoothHost {
    /// A response about the active pairing passkey
    PasskeyMessage(ResponseToPasskey),
}

#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
/// The user response to a bluetooth passkey
pub enum ResponseToPasskey {
    /// The passkey is accepted
    Yes,
    /// The passkey is not accepted
    No,
    /// The process is canceled by the user
    Cancel,
    /// Waiting on the user to decide
    Waiting,
}

/// Responses issued by the library
pub enum BluetoothResponse {
    /// The number of bluetooth adapters detected
    Adapters(usize),
}

/// Settings for an rfcomm profile
#[derive(Clone, Debug)]
pub struct BluetoothRfcommProfileSettings {
    /// The uuid for the profile
    pub uuid: String,
    /// User readable name for the profile
    pub name: Option<String>,
    /// The service uuid for the profile (can be the same as service)
    pub service_uuid: Option<String>,
    /// The channel to use
    pub channel: Option<u16>,
    /// PSM number used for UUIDS and SDP (if applicable)
    pub psm: Option<u16>,
    /// Is authentication required for a connection
    pub authenticate: Option<bool>,
    /// Is authorization required for a connection
    pub authorize: Option<bool>,
    /// For client profiles, This will force connection of the channel when a remote device is connected
    pub auto_connect: Option<bool>,
    /// manual SDP record
    pub sdp_record: Option<String>,
    /// SDP version
    pub sdp_version: Option<u16>,
    /// SDP profile features
    pub sdp_features: Option<u16>,
}

/// Settings for an rfcomm profile
#[derive(Clone)]
pub struct BluetoothL2capProfileSettings {
    /// The uuid for the profile
    pub uuid: String,
    /// User readable name for the profile
    pub name: Option<String>,
    /// The service uuid for the profile (can be the same as service)
    pub service_uuid: Option<String>,
    /// The channel to use
    pub channel: Option<u16>,
    /// PSM number used for UUIDS and SDP (if applicable)
    pub psm: Option<u16>,
    /// Is authentication required for a connection
    pub authenticate: Option<bool>,
    /// Is authorization required for a connection
    pub authorize: Option<bool>,
    /// For client profiles, This will force connection of the channel when a remote device is connected
    pub auto_connect: Option<bool>,
    /// manual SDP record
    pub sdp_record: Option<String>,
    /// SDP version
    pub sdp_version: Option<u16>,
    /// SDP profile features
    pub sdp_features: Option<u16>,
}

/// The trait that implements managing when bluetooth discovery is enabled
#[enum_dispatch::enum_dispatch]
pub trait BluetoothDiscoveryTrait {}

/// The trait for the object that manages bluetooth discovery
#[enum_dispatch::enum_dispatch(BluetoothDiscoveryTrait)]
pub enum BluetoothDiscovery {
    /// The android version
    #[cfg(target_os = "android")]
    Android(android::BluetoothDiscovery),
    /// Linux bluez library implementation
    #[cfg(target_os = "linux")]
    Bluez(linux::BluetoothDiscovery),
    /// Windows implementation
    #[cfg(target_os = "windows")]
    Windows(windows::BluetoothDiscovery),
}

/// The address of a bluetooth adapter
pub enum BluetoothAdapterAddress {
    /// The address in string form
    String(String),
    /// The address in byte form
    Byte([u8; 6]),
}

/// Common async functionality for the bluetooth adapter
#[enum_dispatch::enum_dispatch]
#[async_trait::async_trait]
pub trait AsyncBluetoothAdapterTrait {
    /// Attempt to register a new rfcomm profile
    async fn register_rfcomm_profile(
        &self,
        settings: BluetoothRfcommProfileSettings,
    ) -> Result<BluetoothRfcommProfileAsync, String>;
    /// Attempt to register a new l2cap profile
    async fn register_l2cap_profile(
        &self,
        settings: BluetoothL2capProfileSettings,
    ) -> Result<BluetoothL2capProfileAsync, String>;
    ///Get a list of paired bluetooth devices
    async fn get_paired_devices(&self) -> Option<Vec<BluetoothDevice>>;
    /// Start discovery of bluetooth devices. Run this and drop the result to cancel discovery
    fn start_discovery(&self) -> BluetoothDiscovery;
    /// Get the mac addresses of all bluetooth adapters for the system
    async fn addresses(&self) -> Vec<BluetoothAdapterAddress>;
    /// Set the discoverable property
    async fn set_discoverable(&self, d: bool) -> Result<(), ()>;
}

/// Common sync functionality for the bluetooth adapter
#[enum_dispatch::enum_dispatch]
pub trait SyncBluetoothAdapterTrait {
    /// Attempt to register a new rfcomm profile
    fn register_rfcomm_profile(
        &self,
        settings: BluetoothRfcommProfileSettings,
    ) -> Result<BluetoothRfcommProfileSync, String>;
    /// Attempt to register a new lc2ap profile
    fn register_l2cap_profile(
        &self,
        settings: BluetoothL2capProfileSettings,
    ) -> Result<BluetoothL2capProfileAsync, String>;
    ///Get a list of paired bluetooth devices
    fn get_paired_devices(&self) -> Option<Vec<BluetoothDevice>>;
    /// Start discovery of bluetooth devices. Run this and drop the result to cancel discovery
    fn start_discovery(&self) -> BluetoothDiscovery;
    /// Get the mac addresses of all bluetooth adapters for the system
    fn addresses(&self) -> Vec<BluetoothAdapterAddress>;
    /// Set the discoverable property
    fn set_discoverable(&self, d: bool) -> Result<(), ()>;
}

/// Common functionality for the bluetooth adapter
#[enum_dispatch::enum_dispatch]
pub trait BluetoothAdapterTrait {
    /// Returns Some when the async interface is supported
    fn supports_async(&self) -> Option<&dyn AsyncBluetoothAdapterTrait>;
    /// Returns Some when the sync interface is supported
    fn supports_sync(&self) -> Option<&dyn SyncBluetoothAdapterTrait>;
}

/// The pairing status of a bluetooth device
pub enum PairingStatus {
    /// The device is not paired
    NotPaired,
    /// The device is in the pairing process
    Pairing,
    /// The device is paired
    Paired,
    /// The status is unknown or invalid
    Unknown,
}

fn uuid16(uuid: u16) -> Vec<u8> {
    let mut v = Vec::new();
    v.push(0x19); // UUID-16 type
    v.extend_from_slice(&uuid.to_be_bytes());
    v
}

fn build_sdp_request(uuid: u16, transaction_id: u16) -> Vec<u8> {
    let mut pdu = Vec::new();

    // PDU ID
    pdu.push(0x06);

    // placeholder length
    let mut params = Vec::new();

    // Transaction ID
    params.extend_from_slice(&transaction_id.to_be_bytes());

    // Parameter length placeholder (filled later)

    // ServiceSearchPattern: UUID 0x1101 (SPP)
    let uuid = uuid16(uuid);
    let mut search = Vec::new();
    search.push(0x35); // sequence
    search.push(uuid.len() as u8);
    search.extend_from_slice(&uuid);
    params.extend_from_slice(&search);

    // Attribute ID list (0x0000 - 0xFFFF for everything)
    let mut attrs = Vec::new();
    attrs.push(0x35);
    attrs.push(7);
    attrs.extend_from_slice(&[
        0x09, 0x00, 0x00, // uint16 0x0000
        0x09, 0xFF, 0xFF, // uint16 0xFFFF
    ]);
    params.extend_from_slice(&attrs);

    // Max attribute bytes
    params.extend_from_slice(&0xFFFFu16.to_be_bytes());

    // Continuation state
    params.push(0x00);

    // Now patch length
    let len = params.len() as u16;
    let mut out = Vec::new();
    out.push(0x06);
    out.extend_from_slice(&transaction_id.to_be_bytes());
    out.extend_from_slice(&len.to_be_bytes());
    out.extend_from_slice(&params);

    out
}

#[async_trait::async_trait]
#[enum_dispatch::enum_dispatch]
/// Holds the async functions for a bluetooth device
pub trait BluetoothDeviceAsyncTrait {
    /// Get all known uuids for this device
    async fn get_uuids(&mut self) -> Result<Vec<BluetoothUuid>, std::io::Error>;
    /// Retrieve the device name
    async fn get_name(&self) -> Result<String, std::io::Error>;
    /// Retrieve the device pairing status
    async fn get_pair_state(&self) -> Result<PairingStatus, std::io::Error>;
}

#[enum_dispatch::enum_dispatch]
/// Holds the sync functions for a bluetooth device
pub trait BluetoothDeviceSyncTrait {
    /// Get all known uuids for this device
    fn get_uuids(&mut self) -> Result<Vec<BluetoothUuid>, std::io::Error>;
    /// Retrieve the device name
    fn get_name(&self) -> Result<String, std::io::Error>;
    /// Retrieve the device pairing status
    fn get_pair_state(&self) -> Result<PairingStatus, std::io::Error>;
}

/// The trait that all bluetooth devices must implement
#[enum_dispatch::enum_dispatch]
pub trait BluetoothDeviceTrait {
    /// Does the device support async?
    fn supports_async(&mut self) -> Option<&mut dyn BluetoothDeviceAsyncTrait>;
    /// Does the device support sync?
    fn supports_sync(&mut self) -> Option<&mut dyn BluetoothDeviceSyncTrait>;
    /// Retrieve the device address
    fn get_address(&mut self) -> Result<String, std::io::Error>;
    /// Attempt to get an rfcomm socket for the given uuid and security setting
    fn get_rfcomm_socket(
        &mut self,
        channel: u8,
        is_secure: bool,
    ) -> Result<BluetoothSocket, String>;

    /// Attempt to get an l2cap socket for the given uuid and security setting
    fn get_l2cap_socket(&mut self, psm: u16, is_secure: bool) -> Result<BluetoothSocket, String>;

    /// Run the service discovery protocol
    fn run_sdp(&mut self, uuid: BluetoothUuid) -> Result<sdp::ServiceRecord, String> {
        if let Ok(a) = self.get_address() {
            return sdp::run_sdp(&a, uuid.get_16_bit_id()).map_err(|e| e.to_string());
        }
        Err("Sdp failed".to_string())
    }
}

/// A bluetooth device
#[enum_dispatch::enum_dispatch(BluetoothDeviceTrait)]
pub enum BluetoothDevice {
    /// Bluetooth device on android
    #[cfg(target_os = "android")]
    Android(android::BluetoothDevice),
    /// Bluetooth device on linux using the bluez library
    #[cfg(target_os = "linux")]
    Bluez(linux::LinuxBluetoothDevice),
    /// Bluetooth device on Windows
    #[cfg(target_os = "windows")]
    Windows(windows::BluetoothDevice),
}

/// Represents a bluetooth adapter that communicates to bluetooth devices
#[enum_dispatch::enum_dispatch(BluetoothAdapterTrait)]
pub enum BluetoothAdapter {
    /// The bluetooth adapter for android systems
    #[cfg(target_os = "android")]
    Android(android::Bluetooth),
    /// On linux, bluetooth adapter using the bluez library
    #[cfg(target_os = "linux")]
    Bluez(linux::BluetoothHandler),
    /// On Windows, bluetooth adapter using the windows crate
    #[cfg(target_os = "windows")]
    Windows(windows::BluetoothHandler),
}

/// A builder for `BluetoothAdapter`
pub struct BluetoothAdapterBuilder {
    /// The androidapp object
    #[cfg(target_os = "android")]
    app: Option<AndroidApp>,
    /// The sender to send messages to the bluetooth host
    s: Option<tokio::sync::mpsc::Sender<MessageToBluetoothHost>>,
}

impl Default for BluetoothAdapterBuilder {
    fn default() -> Self {
        Self::new()
    }
}

impl BluetoothAdapterBuilder {
    /// Construct a new self
    pub fn new() -> Self {
        Self {
            #[cfg(target_os = "android")]
            app: None,
            s: None,
        }
    }

    /// Put the required `AndroidApp` object into the builder
    #[cfg(target_os = "android")]
    pub fn with_android_app(&mut self, app: AndroidApp) {
        self.app = Some(app);
    }

    /// Add the sender to the builder
    pub fn with_sender(&mut self, s: tokio::sync::mpsc::Sender<MessageToBluetoothHost>) {
        self.s = Some(s);
    }

    /// Do the build
    pub fn build(self) -> Result<BluetoothAdapter, String> {
        #[cfg(target_os = "android")]
        {
            return Ok(BluetoothAdapter::Android(android::Bluetooth::new(
                self.app.unwrap(),
            )));
        }
        Err("No synchronous builders available".to_string())
    }

    /// Do the build
    pub async fn async_build(self) -> Result<BluetoothAdapter, String> {
        #[cfg(target_os = "android")]
        {
            return self.build();
        }
        #[cfg(target_os = "linux")]
        {
            return Ok(BluetoothAdapter::Bluez(
                linux::BluetoothHandler::new(self.s.unwrap()).await?,
            ));
        }
        #[cfg(target_os = "windows")]
        {
            return Ok(BluetoothAdapter::Windows(
                windows::BluetoothHandler::new(self.s.unwrap()).await?,
            ));
        }
        Err("No async builders available".to_string())
    }
}

/// An active stream for bluetooth communications
pub enum BluetoothStream {
    /// On linux, a stream using the bluez library
    #[cfg(target_os = "linux")]
    Bluez(std::pin::Pin<Box<bluer::rfcomm::Stream>>),
    /// Android code for a bluetooth stream
    #[cfg(target_os = "android")]
    Android(android::RfcommStream),
    /// Windows RFCOMM stream
    #[cfg(target_os = "windows")]
    Windows(windows::WindowsRfcommStream),
}

macro_rules! pin_match {
    ($this:expr, $s:ident => $body:expr) => {
        match $this.get_mut() {
            #[cfg(target_os = "linux")]
            BluetoothStream::Bluez($s) => $body,

            #[cfg(target_os = "android")]
            BluetoothStream::Android($s) => $body,

            #[cfg(target_os = "windows")]
            BluetoothStream::Windows($s) => $body,
        }
    };
}

impl tokio::io::AsyncWrite for BluetoothStream {
    fn poll_write(
        self: std::pin::Pin<&mut Self>,
        cx: &mut std::task::Context<'_>,
        buf: &[u8],
    ) -> std::task::Poll<std::io::Result<usize>> {
        pin_match!(self, s => {
            // SAFETY: we delegate to inner stream directly
            tokio::io::AsyncWrite::poll_write(std::pin::Pin::new(s), cx, buf)
        })
    }

    fn poll_flush(
        self: std::pin::Pin<&mut Self>,
        cx: &mut std::task::Context<'_>,
    ) -> std::task::Poll<std::io::Result<()>> {
        pin_match!(self, s => {
            tokio::io::AsyncWrite::poll_flush(std::pin::Pin::new(s), cx)
        })
    }

    fn poll_shutdown(
        self: std::pin::Pin<&mut Self>,
        cx: &mut std::task::Context<'_>,
    ) -> std::task::Poll<std::io::Result<()>> {
        pin_match!(self, s => {
            tokio::io::AsyncWrite::poll_shutdown(std::pin::Pin::new(s), cx)
        })
    }
}

impl tokio::io::AsyncRead for BluetoothStream {
    fn poll_read(
        self: std::pin::Pin<&mut Self>,
        cx: &mut std::task::Context<'_>,
        buf: &mut tokio::io::ReadBuf<'_>,
    ) -> std::task::Poll<std::io::Result<()>> {
        pin_match!(self, s => {
            tokio::io::AsyncRead::poll_read(std::pin::Pin::new(s), cx, buf)
        })
    }
}

impl BluetoothStream {
    /// Used to check to see if the object supports async read, and then use the functionality
    pub fn supports_async_read(&mut self) -> Option<&mut dyn tokio::io::AsyncRead> {
        match self {
            #[cfg(target_os = "linux")]
            BluetoothStream::Bluez(pin) => Some(pin),
            #[cfg(target_os = "android")]
            BluetoothStream::Android(_pin) => None,
            #[cfg(target_os = "windows")]
            BluetoothStream::Windows(_pin) => None,
        }
    }

    /// Used to check to see if the object supports async write, and then use the functionality
    pub fn supports_async_write(&mut self) -> Option<&mut dyn tokio::io::AsyncWrite> {
        match self {
            #[cfg(target_os = "linux")]
            BluetoothStream::Bluez(pin) => Some(pin),
            #[cfg(target_os = "android")]
            BluetoothStream::Android(_pin) => None,
            #[cfg(target_os = "windows")]
            BluetoothStream::Windows(_pin) => None,
        }
    }

    /// Used to try to use synchronous read functionality
    pub fn supports_sync_read(&mut self) -> Option<&mut dyn std::io::Read> {
        match self {
            #[cfg(target_os = "linux")]
            BluetoothStream::Bluez(_pin) => None,
            #[cfg(target_os = "android")]
            BluetoothStream::Android(pin) => Some(pin),
            #[cfg(target_os = "windows")]
            BluetoothStream::Windows(pin) => Some(pin),
        }
    }

    /// Used to try to use synchronous write functionality
    pub fn supports_sync_write(&mut self) -> Option<&mut dyn std::io::Write> {
        match self {
            #[cfg(target_os = "linux")]
            BluetoothStream::Bluez(_pin) => None,
            #[cfg(target_os = "android")]
            BluetoothStream::Android(pin) => Some(pin),
            #[cfg(target_os = "windows")]
            BluetoothStream::Windows(pin) => Some(pin),
        }
    }
}

/// The trait for bluetooth rfcomm objects that can be connected or accepted
#[async_trait::async_trait]
#[enum_dispatch::enum_dispatch]
pub trait BluetoothRfcommConnectableAsyncTrait {
    /// Accept a connection from a bluetooth peer, returns the stream, bluetooth address, and port
    async fn accept(self) -> Result<(BluetoothStream, [u8; 6], u8), String>;
}

/// A bluetooth profile for rfcomm channels
#[enum_dispatch::enum_dispatch(BluetoothRfcommConnectableAsyncTrait)]
pub enum BluetoothRfcommConnectableAsync {
    /// The android object for the profile
    #[cfg(target_os = "android")]
    Android(android::BluetoothRfcommConnectable),
    /// The bluez library in linux is responsible for the profile
    #[cfg(target_os = "linux")]
    Bluez(bluer::rfcomm::ConnectRequest),
    /// Windows RFCOMM connectable
    #[cfg(target_os = "windows")]
    Windows(windows::BluetoothRfcommConnectable),
}

/// The trait for bluetooth rfcomm objects that can be connected or accepted
#[enum_dispatch::enum_dispatch]
pub trait BluetoothRfcommConnectableSyncTrait {
    /// Accept a connection from a bluetooth peer
    fn accept(self, timeout: std::time::Duration)
    -> Result<(BluetoothStream, [u8; 6], u8), String>;
}

/// A bluetooth profile for rfcomm channels
#[enum_dispatch::enum_dispatch(BluetoothRfcommConnectableSyncTrait)]
pub enum BluetoothRfcommConnectableSync {
    /// The android object for the profile
    #[cfg(target_os = "android")]
    Android(android::BluetoothRfcommConnectable),
}

/// The trait for bluetooth rfcomm objects that can be connected or accepted
#[enum_dispatch::enum_dispatch]
pub trait BluetoothL2capConnectableAsyncTrait {
    /// Accept a connection from a bluetooth peer
    async fn accept(self) -> Result<BluetoothStream, String>;
}

/// A bluetooth profile for rfcomm channels
#[enum_dispatch::enum_dispatch(BluetoothL2capConnectableTrait)]
pub enum BluetoothL2capConnectableAsync {
    /// The android object for the profile
    #[cfg(target_os = "android")]
    Android(android::BluetoothRfcommConnectable),
    /// The bluez library in linux is responsible for the profile
    #[cfg(target_os = "linux")]
    Bluez(bluer::rfcomm::ConnectRequest),
}

/// The trait for bluetooth rfcomm objects that can be connected or accepted
#[enum_dispatch::enum_dispatch]
pub trait BluetoothL2capConnectableSyncTrait {
    /// Accept a connection from a bluetooth peer
    fn accept(self, timeout: std::time::Duration) -> Result<BluetoothStream, String>;
}

/// A bluetooth profile for rfcomm channels
#[enum_dispatch::enum_dispatch(BluetoothL2capConnectableSyncTrait)]
pub enum BluetoothL2capConnectableSync {
    /// The android object for the profile
    #[cfg(target_os = "android")]
    Android(android::BluetoothRfcommConnectable),
}

/// Allows building an object to connect to bluetooth devices
#[enum_dispatch::enum_dispatch]
pub trait BluetoothRfcommProfileAsyncTrait {
    /// Get an object in order to accept a connection from or connect to a bluetooth peer
    async fn connectable(&mut self) -> Result<BluetoothRfcommConnectableAsync, String>;
}

/// Allows building an object to connect to bluetooth devices
#[enum_dispatch::enum_dispatch]
pub trait BluetoothRfcommProfileSyncTrait {
    /// Get an object in order to accept a connection from or connect to a bluetooth peer
    fn connectable(&mut self) -> Result<BluetoothRfcommConnectableSync, String>;
}

/// A bluetooth profile for rfcomm channels
#[enum_dispatch::enum_dispatch(BluetoothRfcommProfileAsyncTrait)]
pub enum BluetoothRfcommProfileAsync {
    /// The bluez library in linux is responsible for the profile
    #[cfg(target_os = "linux")]
    Bluez(bluer::rfcomm::ProfileHandle),
    /// Windows RFCOMM profile
    #[cfg(target_os = "windows")]
    Windows(windows::BluetoothRfcommProfile),
    /// A dummy handler
    Dummy(Dummy),
}

/// A bluetooth profile for rfcomm channels
#[enum_dispatch::enum_dispatch(BluetoothRfcommProfileSyncTrait)]
pub enum BluetoothRfcommProfileSync {
    /// Android rfcomm profile
    #[cfg(target_os = "android")]
    Android(android::BluetoothRfcommProfile),
    /// A dummy handler
    Dummy(Dummy),
}

/// A bluetooth profile for rfcomm channels
#[enum_dispatch::enum_dispatch(BluetoothL2capProfileAsyncTrait)]
pub enum BluetoothL2capProfileAsync {
    /// The bluez library in linux is responsible for the profile
    #[cfg(target_os = "linux")]
    Bluez(bluer::rfcomm::ProfileHandle),
    /// A dummy handler
    Dummy(Dummy),
}

/// A bluetooth profile for rfcomm channels
#[enum_dispatch::enum_dispatch(BluetoothL2capProfileSyncTrait)]
pub enum BluetoothL2capProfileSync {
    /// Android rfcomm profile
    #[cfg(target_os = "android")]
    Android(android::BluetoothRfcommProfile),
    /// A dummy handler
    Dummy(Dummy),
}

/// A dummy struct for ensuring enums are not empty
pub struct Dummy {}

impl BluetoothRfcommProfileSyncTrait for Dummy {
    fn connectable(&mut self) -> Result<BluetoothRfcommConnectableSync, String> {
        unimplemented!()
    }
}

impl BluetoothRfcommProfileAsyncTrait for Dummy {
    async fn connectable(&mut self) -> Result<BluetoothRfcommConnectableAsync, String> {
        unimplemented!()
    }
}

/// A trait combining read and write functionality
pub trait AsyncReadWrite: tokio::io::AsyncRead + tokio::io::AsyncWrite + Unpin + Send {}
impl<T: tokio::io::AsyncRead + tokio::io::AsyncWrite + Unpin + Send> AsyncReadWrite for T {}

/// A trait combining read and write functionality
pub trait SyncReadWrite: std::io::Read + std::io::Write + Send {}
impl<T: std::io::Read + std::io::Write + Unpin + Send> SyncReadWrite for T {}


/// The common functions for all bluetooth rfcomm sockets
#[async_trait::async_trait]
#[enum_dispatch::enum_dispatch]
pub trait BluetoothSocketTrait {
    /// Is the socket connected
    fn is_connected(&self) -> Result<bool, std::io::Error>;
    /// connect the socket
    async fn async_connect(&mut self) -> Result<(), std::io::Error>;
    /// connect the socket
    fn sync_connect(&mut self) -> Result<(), std::io::Error>;
    /// Does the socket support async?
    fn supports_async(&mut self) -> Option<&mut dyn AsyncReadWrite> {
        None
    }
    /// Does the socket support sync?
    fn supports_sync(&mut self) -> Option<&mut dyn SyncReadWrite> {
        None
    }
}

/// A bluetooth rfcomm socket
#[enum_dispatch::enum_dispatch(BluetoothSocketTrait)]
pub enum BluetoothSocket {
    /// The android based rfcomm socket
    #[cfg(target_os = "android")]
    Android(android::BluetoothSocket),
    /// Linux using bluez library
    #[cfg(target_os = "linux")]
    Bluez(linux::BluetoothRfcommSocket),
    /// Windows bluetooth socket
    #[cfg(target_os = "windows")]
    Windows(windows::BluetoothRfcommSocket),
}
