//! Wrapper around the NRF24 driver which provides tokio-compatible asynchronous interfaces.

use log::info;
use protocol::{Location, Packet};
use tokio::sync::mpsc::{self, UnboundedReceiver, UnboundedSender};

#[cfg(feature = "nrf24l01-stick")]
mod nrfstick;
#[cfg(feature = "nrf24l01-stick")]
pub use nrfstick::RadioError;
#[cfg(feature = "nrf24l01-stick")]
pub use nrfstick::RadioTask;

#[cfg(any(feature = "nanopineo", feature = "rpi4"))]
mod gpio;
#[cfg(any(feature = "nanopineo", feature = "rpi4"))]
pub use gpio::RadioError;
#[cfg(any(feature = "nanopineo", feature = "rpi4"))]
pub use gpio::RadioTask;

const DISPLAY_ID: u8 = 0x20;
const DISPLAY_KEY: [u8; 16] = include!("../../../../common/display_key.txt");
const WEATHER_STATION_0_ID: u8 = 0x30;
const WEATHER_STATION_0_KEY: [u8; 16] = include!("../../../../common/weather_station_0_key.txt");
const WEATHER_STATION_1_ID: u8 = 0x31;
const WEATHER_STATION_1_KEY: [u8; 16] = include!("../../../../common/weather_station_1_key.txt");

/// Wrapper around the NRF24 radio driver.
pub struct Radio {
    tx: UnboundedSender<(u8, Packet)>,
}

impl Radio {
    /// Initializes the radio and returns both radio and the stream of incoming packets.
    pub async fn init() -> Result<(Radio, UnboundedReceiver<(u8, Packet)>), RadioError> {
        info!("Initializing radio...");
        let (tx_send, tx_recv) = mpsc::unbounded_channel();
        let (rx_send, rx_recv) = mpsc::unbounded_channel();
        let mut task = RadioTask::new(tx_recv, rx_send);
        task.init().await?;
        info!("radio initialized.");

        // Start a task for packet handling.
        tokio::spawn(async move {
            task.run().await;
        });

        Ok((Radio { tx: tx_send }, rx_recv))
    }

    /// Sends a packet to the specified device.
    ///
    /// The function returns immediately, and the packet might or might not be sent successfully.
    ///
    /// # Arguments
    ///
    /// * `device_id` - the ID of the targeted device
    /// * `packet` - the packet to send
    pub fn send_packet_async(&mut self, device_id: u8, packet: Packet) {
        // TODO: Can we encrypt the packet here (and deduplicate the code)?
        self.tx.send((device_id, packet)).unwrap();
    }
}

pub fn get_device_location(device_id: u8) -> Location {
    match device_id {
        DISPLAY_ID => Location::Bedroom,
        WEATHER_STATION_0_ID => Location::Livingroom,
        WEATHER_STATION_1_ID => Location::Bathroom,
        _ => Location::Livingroom,
    }
}

fn get_key(device_id: u8) -> Option<&'static [u8]> {
    match device_id {
        DISPLAY_ID => Some(&DISPLAY_KEY),
        WEATHER_STATION_0_ID => Some(&WEATHER_STATION_0_KEY),
        WEATHER_STATION_1_ID => Some(&WEATHER_STATION_1_KEY),
        _ => None,
    }
}
