use rf4463::{
    config::{RADIO_CONFIG_500_2, RADIO_CONFIG_5_4},
    Rf4463, TransferError,
};
use rppal::{
    gpio::{Gpio, OutputPin},
    hal::Delay,
    spi::{self, Bus, Mode, SlaveSelect, Spi},
};
use serial::{unix::TTYPort, BaudRate::BaudOther, SerialPort};
use std::{
    io::{Read, Write},
    thread::sleep,
    time::Duration,
};

use crate::packet::FecPacket;

// Used for talking to an RF4463 via a microcontroller
// The microcontroller takes in packet data over UART and sends it to the RF4463
pub struct UartRadio {
    uart: TTYPort,
}

impl UartRadio {
    pub fn new(uart: &str) -> anyhow::Result<Self> {
        let mut uart = serial::open(uart)?;
        uart.reconfigure(&|settings| settings.set_baud_rate(BaudOther(921_600)))?;

        Ok(Self { uart })
    }

    pub fn send_packet(&mut self, packet: &FecPacket) -> anyhow::Result<()> {
        loop {
            self.uart.write_all(&[0x00])?;
            self.uart.flush()?;

            let mut buf = [0; 1];
            self.uart.read_exact(&mut buf)?;

            if buf == [0x02] {
                break; // enough space for packet
            } else {
                sleep(Duration::from_millis(10));
            }
        }

        self.uart.write_all(&[0x01])?;
        self.uart.write_all(&packet.0)?;

        Ok(())
    }
}

pub struct SpiRadio {
    radio: Rf4463<Spi, OutputPin, OutputPin, Delay>,
    failures: u32,
    total: u32,
}

impl SpiRadio {
    pub fn new() -> anyhow::Result<Self> {
        let spi = Spi::new(Bus::Spi0, SlaveSelect::Ss0, 10_000_000, Mode::Mode0)?;
        let gpio = Gpio::new()?;
        let sdn = gpio.get(22)?.into_output();
        let cs = gpio.get(24)?.into_output();

        let delay = Delay::new();

        // todo fix this unwrap
        let radio = Rf4463::new(spi, sdn, cs, delay, &mut RADIO_CONFIG_500_2.clone()).unwrap();

        Ok(Self {
            radio,
            failures: 0,
            total: 0,
        })
    }

    pub fn send_packet(&mut self, packet: &FecPacket) -> anyhow::Result<()> {
        self.total += 1;

        // todo fix this unwrap
        if self.actually_send(packet, 3).is_err() {
            self.failures += 1;

            println!(
                "{} % fail rate",
                (self.failures as f64) / (self.total as f64)
            );
        }

        Ok(())
    }

    fn actually_send(
        &mut self,
        packet: &FecPacket,
        tries: u32,
    ) -> Result<(), TransferError<spi::Error>> {
        if tries == 0 {
            return Err(TransferError::FifoOverflow);
        }

        match self.radio.tx(&mut packet.0.clone()) {
            Ok(()) => Ok(()),
            Err(TransferError::FifoOverflow) => self.actually_send(packet, tries - 1),
            Err(e) => Err(e),
        }
    }
}