//! HCI Hardware Abstraction Layer
//! Supports sending HCI commands to the HAL and receving
//! HCI events from the HAL
#[cfg(target_os = "android")]
#[macro_use]
extern crate lazy_static;

pub mod facade;
pub mod rootcanal_hal;
pub mod snoop;

#[cfg(target_os = "android")]
mod hidl_hal;

use gddi::module;
use thiserror::Error;

#[cfg(target_os = "android")]
module! {
    hal_module,
    submodules {
        facade::hal_facade_module,
        hidl_hal::hidl_hal_module,
        snoop::snoop_module,
    },
}

#[cfg(not(target_os = "android"))]
module! {
    hal_module,
    submodules {
        facade::hal_facade_module,
        rootcanal_hal::rootcanal_hal_module,
        snoop::snoop_module,
    },
}
/// H4 packet header size
const H4_HEADER_SIZE: usize = 1;

pub use snoop::AclHal;
pub use snoop::ControlHal;
pub use snoop::IsoHal;

mod internal {
    use bt_packets::hci::{AclPacket, CommandPacket, EventPacket, IsoPacket};
    use gddi::Stoppable;
    use std::sync::Arc;
    use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender};
    use tokio::sync::Mutex;

    #[derive(Clone, Stoppable)]
    pub struct RawHal {
        pub cmd_tx: UnboundedSender<CommandPacket>,
        pub evt_rx: Arc<Mutex<UnboundedReceiver<EventPacket>>>,
        pub acl_tx: UnboundedSender<AclPacket>,
        pub acl_rx: Arc<Mutex<UnboundedReceiver<AclPacket>>>,
        pub iso_tx: UnboundedSender<IsoPacket>,
        pub iso_rx: Arc<Mutex<UnboundedReceiver<IsoPacket>>>,
    }

    pub struct InnerHal {
        pub cmd_rx: UnboundedReceiver<CommandPacket>,
        pub evt_tx: UnboundedSender<EventPacket>,
        pub acl_rx: UnboundedReceiver<AclPacket>,
        pub acl_tx: UnboundedSender<AclPacket>,
        pub iso_rx: UnboundedReceiver<IsoPacket>,
        pub iso_tx: UnboundedSender<IsoPacket>,
    }

    impl InnerHal {
        pub fn new() -> (RawHal, Self) {
            let (cmd_tx, cmd_rx) = unbounded_channel();
            let (evt_tx, evt_rx) = unbounded_channel();
            let (acl_down_tx, acl_down_rx) = unbounded_channel();
            let (iso_down_tx, iso_down_rx) = unbounded_channel();
            let (acl_up_tx, acl_up_rx) = unbounded_channel();
            let (iso_up_tx, iso_up_rx) = unbounded_channel();
            (
                RawHal {
                    cmd_tx,
                    evt_rx: Arc::new(Mutex::new(evt_rx)),
                    acl_tx: acl_down_tx,
                    acl_rx: Arc::new(Mutex::new(acl_up_rx)),
                    iso_tx: iso_down_tx,
                    iso_rx: Arc::new(Mutex::new(iso_up_rx)),
                },
                Self {
                    cmd_rx,
                    evt_tx,
                    acl_rx: acl_down_rx,
                    acl_tx: acl_up_tx,
                    iso_rx: iso_down_rx,
                    iso_tx: iso_up_tx,
                },
            )
        }
    }
}

/// Result type
type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;

/// Errors that can be encountered while dealing with the HAL
#[derive(Error, Debug)]
pub enum HalError {
    /// Invalid rootcanal host error
    #[error("Invalid rootcanal host")]
    InvalidAddressError,
    /// Error while connecting to rootcanal
    #[error("Connection to rootcanal failed: {0}")]
    RootcanalConnectError(#[from] tokio::io::Error),
}