//! It demonstrates LORA P2P send functionality.
//! And is based of the example in https://github.com/lora-rs/lora-rs
#![no_std]
#![no_main]

mod shared;

use esp_backtrace as _;
use esp_println::println;

use embassy_time::{Duration, Timer};

use embassy_executor::Spawner;
use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, mutex::Mutex};
use embassy_time::Delay;

use esp_hal::gpio::{Input, Level, Output, Pull};
use esp_hal::spi::{master::Config, master::Spi, Mode};
use esp_hal::Async;
use esp_hal::{clock::CpuClock, timer::timg::TimerGroup};

use fugit::RateExtU32;
use static_cell::StaticCell;

use lora_phy::iv::GenericSx127xInterfaceVariant;
use lora_phy::mod_params::{Bandwidth, CodingRate, SpreadingFactor};
use lora_phy::sx127x::{Sx1276, Sx127x};
use lora_phy::{sx127x, LoRa};

use lora_phy::RxMode;

const LORA_FREQUENCY_IN_HZ: u32 = 923_200_000; // WARNING: Set this appropriately for the region

static SPI_BUS: StaticCell<
    Mutex<CriticalSectionRawMutex, esp_hal::spi::master::Spi<'static, Async>>,
> = StaticCell::new();

#[esp_hal_embassy::main]
async fn main(_spawner: Spawner) {
    // Initialize line number for logging
    let mut line_number = 0;

    // Initialize the logger
    esp_println::logger::init_logger_from_env();
    println!("Starting LoRa P2P example");

    // Set up ESP32
    let peripherals = esp_hal::init(esp_hal::Config::default().with_cpu_clock(CpuClock::max()));
    let timer_group = TimerGroup::new(peripherals.TIMG0);

    esp_hal_embassy::init(timer_group.timer1);
    println!("Send Embassy initialized");

    // Initialize the display
    let mut display =
        match shared::Display::new(peripherals.I2C0, peripherals.GPIO22, peripherals.GPIO21) {
            Ok(display) => display,
            Err(err) => {
                println!("Error initializing display: {:?}", err);
                return;
            }
        };
        
    // Initialize the SPI bus
    let nss = Output::new(peripherals.GPIO18, Level::Low);
    let sclk = peripherals.GPIO5;
    let mosi = peripherals.GPIO27;
    let miso = peripherals.GPIO19;

    // Initialize the sx127x radio pins
    let mut reset = Output::new(peripherals.GPIO23, Level::Low);
    let dio1 = Input::new(peripherals.GPIO26, Pull::None);
    println!("Pins Initialized");

    let spi = Spi::new(
        peripherals.SPI2,
        Config::default()
            .with_frequency(100.kHz())
            .with_mode(Mode::_0),
    )
    .unwrap()
    .with_sck(sclk)
    .with_mosi(mosi)
    .with_miso(miso)
    .into_async();

    // Initialize the static SPI bus
    let spi_bus = SPI_BUS.init(Mutex::new(spi));
    let spi_device = embassy_embedded_hal::shared_bus::asynch::spi::SpiDevice::new(spi_bus, nss);

    // reset.set_low();
    // Timer::after(Duration::from_millis(10)).await;
    // reset.set_high();
    // Timer::after(Duration::from_millis(10)).await;

    println!("Receive SPI Initialized");

    // We use Sx1276
    // Create the SX126x configuration
    let sx127x_config = sx127x::Config {
        chip: Sx1276,     // Specify the SX1276 chip
        tcxo_used: false, // The LoRa32 T3 v1.6.1 uses a crystal oscillator, not a TCXO
        tx_boost: true,   // Use PA_BOOST for higher transmit power (depends on board layout)
        rx_boost: true,
    };
    println!("Send SX127x configuration created");

    // Create the radio instance
    let iv = match GenericSx127xInterfaceVariant::new(reset, dio1, None, None) {
        Ok(interface) => interface,
        Err(err) => {
            println!("Error creating GenericSx127xInterfaceVariant: {:?}", err);
            return;
        }
    };

    // Create the LoRa instance
    let mut lora = match LoRa::new(Sx127x::new(spi_device, iv, sx127x_config), false, Delay).await {
        Ok(lora_instance) => lora_instance,
        Err(err) => {
            println!("Error creating LoRa instance: {:?}", err);
            return;
        }
    };

    println!("Receive Radio Initialized");

    let mut receiving_buffer = [00u8; 100];

    let modulation_params = {
        match lora.create_modulation_params(
            SpreadingFactor::_10,
            Bandwidth::_125KHz,
            CodingRate::_4_8,
            LORA_FREQUENCY_IN_HZ,
        ) {
            Ok(mp) => {
                println!("Modulation parameters created successfully");
                mp
            }
            Err(err) => {
                println!("Error creating modulation parameters: {:?}", err);
                return;
            }
        }
    };

    display.write_text(line_number, "setup successful");
    line_number = line_number + 1;
    Timer::after(Duration::from_millis(200)).await;

    let rx_packet_params = {
        match lora.create_rx_packet_params(
            4,
            false,
            receiving_buffer.len() as u8,
            true,
            false,
            &modulation_params,
        ) {
            Ok(pp) => pp,
            Err(err) => {
                println!("Radio error = {:?}", err);
                return;
            }
        }
    };

    match lora
        .prepare_for_rx(RxMode::Continuous, &modulation_params, &rx_packet_params)
        .await
    {
        Ok(()) => {
            display.write_text(line_number, "preparing successful");
            line_number = line_number + 1;
            Timer::after(Duration::from_millis(200)).await;
        }
        Err(err) => {
            println!("Radio error = {:?}", err);
            return;
        }
    };

    let expected_msg = b"hello";
    let expected_msg_len = expected_msg.len();

    println!("Receive going into loop");

    loop {
        receiving_buffer = [00u8; 100];
        match lora.rx(&rx_packet_params, &mut receiving_buffer).await {
            Ok((received_len, _rx_pkt_status)) => {
                if (received_len == expected_msg_len as u8)
                    && (receiving_buffer[..expected_msg_len] == *expected_msg)
                {
                    display.write_text(line_number, "receive successful");
                    line_number = 0;
                    println!(
                        "rx successful: {:?}",
                        core::str::from_utf8(&receiving_buffer[..received_len as usize]).unwrap()
                    );
                } else {
                    println!("rx unknown packet");
                }
            }
            Err(err) => println!("rx unsuccessful = {:?}", err),
        }
    }
}
