use core::future::Future;

use crate::error::Error;
use crate::frame::parameters::mfglib::{
    end, get_channel, get_power, send_packet, set_channel, set_power, start, start_stream,
    start_tone, stop_stream, stop_tone,
};
use crate::transport::Transport;
use crate::types::ByteSizedVec;

/// The `Mfglib` trait provides an interface for the
/// Manufacturing and Functional Test Library (`MfgLib`) test routines.
pub trait Mfglib {
    /// Deactivate use of `Mfglib` test routines; restores the hardware to the state it was in prior
    /// to [`start()`](Self::start) and stops receiving packets started by [`start()`](Self::start)
    /// at the same time.
    fn end(&mut self) -> impl Future<Output = Result<(), Error>> + Send;

    /// Returns the current radio channel, as previously set via [`set_channel()`](Self::set_channel).
    fn get_channel(&mut self) -> impl Future<Output = Result<u8, Error>> + Send;

    /// Returns the current radio power setting, as previously set via [`set_power()`](Self::set_power).
    fn get_power(&mut self) -> impl Future<Output = Result<i8, Error>> + Send;

    /// Sends a single packet consisting of the following bytes:
    ///     * `packetLength`
    ///     * `packetContents[0]`
    ///     * ...
    ///     * `packetContents[packetLength - 3]`
    ///     * `CRC[0]`
    ///     * `CRC[1]`
    ///
    /// The total number of bytes sent is packetLength + 1.
    /// The radio replaces the last two bytes of `content` with the 16-bit CRC for the packet.
    fn send_packet(
        &mut self,
        content: ByteSizedVec<u8>,
    ) -> impl Future<Output = Result<(), Error>> + Send;

    /// Sets the radio channel.
    ///
    /// Calibration occurs if this is the first time the channel has been used.
    fn set_channel(&mut self, channel: u8) -> impl Future<Output = Result<(), Error>> + Send;

    /// First select the transmit power mode, and then include a method for selecting the radio transmit power.
    ///
    /// The valid power settings depend upon the specific radio in use.
    /// Ember radios have discrete power settings, and then requested power is rounded to a valid
    /// power setting; the actual power output is available to the caller via [`get_power()`](Self::get_power).
    fn set_power(
        &mut self,
        tx_power_mode: u16,
        power: i8,
    ) -> impl Future<Output = Result<(), Error>> + Send;

    /// Activate use of mfglib test routines and enables the radio receiver to report packets it
    /// receives to the [`Rx`](crate::frame::parameters::mfglib::handler::Rx) callback.
    ///
    /// These packets will not be passed up with a CRC failure.
    /// All other mfglib functions will return an error until the [`start()`](Self::start) has been called.
    fn start(&mut self, rx_callback: bool) -> impl Future<Output = Result<(), Error>> + Send;

    /// Starts transmitting a random stream of characters.
    ///
    /// This is so that the radio modulation can be measured.
    fn start_stream(&mut self) -> impl Future<Output = Result<(), Error>> + Send;

    /// Starts transmitting an unmodulated tone on the currently set channel and power level.
    ///
    /// Upon successful return, the tone will be transmitting.
    /// To stop transmitting tone, application must call [`stop_tone()`](Self::stop_tone), allowing
    /// it the flexibility to determine its own criteria or tone duration (time, event, etc.).
    fn start_tone(&mut self) -> impl Future<Output = Result<(), Error>> + Send;

    /// Stops transmitting a random stream of characters started by [`start_stream()`](Self::start_stream).
    fn stop_stream(&mut self) -> impl Future<Output = Result<(), Error>> + Send;

    /// Stops transmitting tone started by [`start_tone()`](Self::start_tone).
    fn stop_tone(&mut self) -> impl Future<Output = Result<(), Error>> + Send;
}

impl<T> Mfglib for T
where
    T: Transport,
{
    async fn end(&mut self) -> Result<(), Error> {
        self.communicate(end::Command).await?.try_into()
    }

    async fn get_channel(&mut self) -> Result<u8, Error> {
        self.communicate(get_channel::Command)
            .await
            .map(|response| response.channel())
    }

    async fn get_power(&mut self) -> Result<i8, Error> {
        self.communicate(get_power::Command)
            .await
            .map(|response| response.power())
    }

    async fn send_packet(&mut self, content: ByteSizedVec<u8>) -> Result<(), Error> {
        self.communicate(send_packet::Command::new(content))
            .await?
            .try_into()
    }

    async fn set_channel(&mut self, channel: u8) -> Result<(), Error> {
        self.communicate(set_channel::Command::new(channel))
            .await?
            .try_into()
    }

    async fn set_power(&mut self, tx_power_mode: u16, power: i8) -> Result<(), Error> {
        self.communicate(set_power::Command::new(tx_power_mode, power))
            .await?
            .try_into()
    }

    async fn start(&mut self, rx_callback: bool) -> Result<(), Error> {
        self.communicate(start::Command::new(rx_callback))
            .await?
            .try_into()
    }

    async fn start_stream(&mut self) -> Result<(), Error> {
        self.communicate(start_stream::Command).await?.try_into()
    }

    async fn start_tone(&mut self) -> Result<(), Error> {
        self.communicate(start_tone::Command).await?.try_into()
    }

    async fn stop_stream(&mut self) -> Result<(), Error> {
        self.communicate(stop_stream::Command).await?.try_into()
    }

    async fn stop_tone(&mut self) -> Result<(), Error> {
        self.communicate(stop_tone::Command).await?.try_into()
    }
}
