use core::pin::pin;
use esp_idf_hal::{
    gpio::{Output, Pin, PinDriver},
    prelude::Peripherals,
    rmt::{PinState, Pulse, PulseTicks, RmtTransmitConfig, VariableLengthSignal},
};
use esp_idf_svc::timer::EspTaskTimerService;

const DEBUG_TIME_DILATOR: u16 = 1;

const DATA: &[u8] = b"TEST TEST TEST";

const HALFBIT_BURST_QUANT: u16 = 10 * DEBUG_TIME_DILATOR;

fn create_frame(data: &Vec<u8>) -> VariableLengthSignal {
    let mut signal = VariableLengthSignal::new();
    signal
        .push(&[Pulse::new(
            PinState::High,
            PulseTicks::new(3 * HALFBIT_BURST_QUANT).unwrap(),
        )])
        .unwrap();

    let mut mb = |b| {
        if b == 0 {
            signal
                .push(&[
                    Pulse::new(PinState::Low, PulseTicks::new(HALFBIT_BURST_QUANT).unwrap()),
                    Pulse::new(
                        PinState::High,
                        PulseTicks::new(HALFBIT_BURST_QUANT).unwrap(),
                    ),
                ])
                .unwrap();
        } else {
            signal
                .push(&[
                    Pulse::new(
                        PinState::High,
                        PulseTicks::new(HALFBIT_BURST_QUANT).unwrap(),
                    ),
                    Pulse::new(PinState::Low, PulseTicks::new(HALFBIT_BURST_QUANT).unwrap()),
                ])
                .unwrap();
        }
    };

    // let mut i = 0;
    for bit in data {
        // i += 1;
        let mut b = *bit;
        for _ in 0..8 {
            for _ in 0..3 {
                // for error correction
                mb(b & 1);
            }
            b >>= 1;
        }
        // if i % 100 == 0 {
        //     log::info!("byte {i}");
        // }
    }

    signal
        .push(&[Pulse::new(
            PinState::Low,
            PulseTicks::new(3 * HALFBIT_BURST_QUANT).unwrap(),
        )])
        .unwrap();
    signal
}

fn main() {
    esp_idf_svc::sys::link_patches();

    esp_idf_svc::log::EspLogger::initialize_default();

    let timer_service = EspTaskTimerService::new().unwrap();
    let peripherals = Peripherals::take().unwrap();

    let mut tx = esp_idf_hal::rmt::TxRmtDriver::new(
        peripherals.rmt.channel0,
        peripherals.pins.gpio26,
        &RmtTransmitConfig::new(),
    )
    .unwrap();

    loop {
        tx.start_blocking(&create_frame(&DATA.to_vec())).unwrap();
        std::thread::sleep(std::time::Duration::from_secs(1));
    }

    //     let mut pin = PinDriver::output(peripherals.pins.gpio26).unwrap();
    //
    //     log::info!("Hello, world!");
    //     log::info!("{}", DATA.len());
    //     pin.set_high().unwrap();
    //
    //     loop {
    //         // send_data(vec![b'N', b'y', b'a', b'a'], &timer_service, &mut pin);
    //         let start = std::time::SystemTime::now();
    //         send_data(DATA.to_vec(), &timer_service, &mut pin);
    //         log::info!(
    //             "sent {} bytes of data in {} µs",
    //             DATA.len(),
    //             start.elapsed().unwrap().as_micros()
    //         );
    //         std::thread::sleep(std::time::Duration::from_secs(1));
    //     }
}

fn send_bit<T>(bit: u8, ts: &EspTaskTimerService, pin: &mut PinDriver<T, Output>)
where
    T: Pin,
{
    let dur1 = bit as u64 * 10 + 20;
    let dur2 = (1 - bit as u64) * 10 + 20;
    pin.set_low().unwrap();
    // log::info!("up");
    esp_idf_svc::hal::task::block_on(pin!(async move {
        ts.timer_async()
            .unwrap()
            .after(std::time::Duration::from_micros(
                dur1, /* * DEBUG_TIME_DILATOR*/
            ))
            .await
            .unwrap()
    }));
    pin.set_high().unwrap();
    // log::info!("down");
    esp_idf_svc::hal::task::block_on(pin!(async move {
        ts.timer_async()
            .unwrap()
            .after(std::time::Duration::from_micros(
                dur2, /* * DEBUG_TIME_DILATOR*/
            ))
            .await
            .unwrap()
    }))
}

fn send_bit_manchester<T>(bit: u8, ts: &EspTaskTimerService, pin: &mut PinDriver<T, Output>)
where
    T: Pin,
{
    if bit == 0 {
        pin.set_low().unwrap();
    } else {
        pin.set_high().unwrap();
    }
    // log::info!("up");
    esp_idf_svc::hal::task::block_on(pin!(async move {
        ts.timer_async()
            .unwrap()
            .after(std::time::Duration::from_micros(
                30, /* * DEBUG_TIME_DILATOR*/
            ))
            .await
            .unwrap()
    }));
    if bit == 0 {
        pin.set_high().unwrap();
    } else {
        pin.set_low().unwrap();
    }
    // log::info!("down");
    esp_idf_svc::hal::task::block_on(pin!(async move {
        ts.timer_async()
            .unwrap()
            .after(std::time::Duration::from_micros(
                30, /* * DEBUG_TIME_DILATOR*/
            ))
            .await
            .unwrap()
    }))
}

fn send_data<T>(data: Vec<u8>, ts: &EspTaskTimerService, pin: &mut PinDriver<T, Output>)
where
    T: Pin,
{
    let mut i = 0;
    for mut b in data {
        i += 1;
        for _ in 0..8 {
            for _ in 0..3 {
                // for error correction
                send_bit_manchester(b & 1, ts, pin);
            }
            b >>= 1;
        }
        if i % 100 == 0 {
            log::info!("byte {i}");
        }
    }
}