//! 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 embassy_embedded_hal::shared_bus::asynch::spi::SpiDevice;
use esp_backtrace as _;
use esp_hal::peripherals::Peripherals;
use esp_println::println;

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::{clock::CpuClock, timer::timg::TimerGroup};
use esp_hal::{peripheral, Async};

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};

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 dio0 = Input::new(peripherals.GPIO26, Pull::None);
    println!("Pins Initialized");

    let spi = Spi::new(
        peripherals.SPI2,
        Config::default()
            .with_frequency(200.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);

    println!("Send SPI initialized");

    // Create the SX127x 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, dio0, 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;
        }
    };

    let modulation_params = {
        match lora.create_modulation_params(
            SpreadingFactor::_10,
            Bandwidth::_125KHz,
            CodingRate::_4_8,
            LORA_FREQUENCY_IN_HZ,
        ) {
            Ok(mp) => mp,
            Err(err) => {
                println!("Radio error = {:?}", err);
                return;
            }
        }
    };

    println!("Send Modulation parameters created");

    let mut tx_packet_params = {
        match lora.create_tx_packet_params(4, false, true, false, &modulation_params) {
            Ok(pp) => pp,
            Err(err) => {
                println!("Radio error = {:?}", err);
                return;
            }
        }
    };

    let buffer = b"hello";

    display.write_text(line_number, "buffer creation successful");
    line_number = line_number + 1;

    match lora
        .prepare_for_tx(&modulation_params, &mut tx_packet_params, 20, buffer)
        .await
    {
        Ok(()) => {
            println!("prepare_for_tx successful");
        }
        Err(err) => {
            println!("Radio error = {:?}", err);
            return;
        }
    };

    match lora.tx().await {
        Ok(()) => {
            display.write_text(line_number, "send of hello successful");
            line_number = line_number + 1;
        }
        Err(err) => {
            println!("Radio error = {:?}", err);
            return;
        }
    };

    match lora.sleep(false).await {
        Ok(()) => {
            display.write_text(line_number, "sleep successful");
            line_number = 0;
        }
        Err(err) => println!("Sleep unsuccessful = {:?}", err),
    }
}
