#![no_std]
#![no_main]
#![feature(impl_trait_in_assoc_type)]
#![feature(type_alias_impl_trait)]

use core::cell::OnceCell;

use defmt::info;
use embassy_executor::Spawner;
use embassy_net::HardwareAddress;
use embassy_sync::{blocking_mutex::NoopMutex, blocking_mutex::raw::NoopRawMutex, mutex::Mutex};
use embassy_time::{Duration, Timer};
use esp_hal::clock::CpuClock;
use esp_hal::timer::systimer::SystemTimer;
use esp_hal::timer::timg::TimerGroup;
use esp_hal_embassy::main;
use esp_wifi::{
    EspWifiController,
    esp_now::{
        self, BROADCAST_ADDRESS, EspNow, EspNowManager, EspNowReceiver, EspNowSender,
        EspNowWithWifiCreateToken,
    },
    init,
    wifi::{
        self, AuthMethod, ClientConfiguration, Configuration, RxControlInfo, WifiController,
        WifiDevice, ap_mac, sta_mac,
    },
};
use panic_rtt_target as _;
use static_cell::StaticCell;

extern crate alloc;

macro_rules! mk_static {
    ($t:ty,$val:expr) => {{
        static STATIC_CELL: static_cell::StaticCell<$t> = static_cell::StaticCell::new();
        #[deny(unused_attributes)]
        let x = STATIC_CELL.uninit().write(($val));
        x
    }};
}

// static EXECUTOR: StaticCell<Executor> = StaticCell::new();
const WIFI_SSID: &str = "P601";
const WIFI_PASSWORD: &str = "00000000";
const WIFI_CHANNEL: Option<u8> = Some(10);

type SharedWifiController<'a> = &'a Mutex<NoopRawMutex, WifiController<'a>>;

#[main]
async fn main(_spawner: Spawner) {
    // generator version: 0.3.1

    rtt_target::rtt_init_defmt!();

    let config = esp_hal::Config::default().with_cpu_clock(CpuClock::max());
    let peripherals = esp_hal::init(config);

    esp_alloc::heap_allocator!(size: 72 * 1024);

    let timer0: SystemTimer = SystemTimer::new(peripherals.SYSTIMER);
    esp_hal_embassy::init(timer0.alarm0);

    info!("Embassy initialized!");

    let timer1 = TimerGroup::new(peripherals.TIMG0);
    let esp_wifi_ctrl = &*mk_static!(
        EspWifiController<'static>,
        init(
            timer1.timer0,
            esp_hal::rng::Rng::new(peripherals.RNG),
            peripherals.RADIO_CLK,
        )
        .unwrap()
    );

    let (wifi, esp_now_with_wifi_token) = esp_now::enable_esp_now_with_wifi(peripherals.WIFI);

    let (mut wifi_controller, wifi_interface) = wifi::new(&esp_wifi_ctrl, wifi).unwrap();
    let wifi_config = Configuration::Client(ClientConfiguration {
        ssid: WIFI_SSID.try_into().unwrap(),
        auth_method: AuthMethod::WPAWPA2Personal,
        password: WIFI_PASSWORD.try_into().unwrap(),
        channel: WIFI_CHANNEL,
        ..Default::default()
    });

    let _ = WifiController::set_configuration(&mut wifi_controller, &wifi_config)
        .expect("Can't set configuration for Wifi Controller");
    let _ = wifi_controller
        .start()
        .expect("Can't start Wifi Controller");
    let _ = wifi_controller.connect().expect("Something is wrong!!!");
    // let wifi_controller = mk_static!(WifiController<'static>, wifi_controller);
    let mac_address_bytes: [u8; 6] = wifi_interface.sta.mac_address();
    info!(
        "Device MAC Address: {:02X}:{:02X}:{:02X}:{:02X}:{:02X}:{:02X}",
        mac_address_bytes[0],
        mac_address_bytes[1],
        mac_address_bytes[2],
        mac_address_bytes[3],
        mac_address_bytes[4],
        mac_address_bytes[5]
    );
    let wifi_controller: SharedWifiController<'static> =
        mk_static!(Mutex<NoopRawMutex, WifiController<'static>>, Mutex::new(wifi_controller));
    info!("WIFI state: {}", wifi::wifi_state());

    let esp_now = EspNow::new_with_wifi(&esp_wifi_ctrl, esp_now_with_wifi_token).unwrap();
    let _ = esp_now.set_channel(WIFI_CHANNEL.unwrap());
    info!("ESP_NOW version {}", esp_now.version().unwrap());
    let (manager, sender, receiver) = esp_now.split();
    let manager = mk_static!(EspNowManager, manager);
    let sender = mk_static!(
        Mutex::<NoopRawMutex, EspNowSender<'static>>,
        Mutex::<NoopRawMutex, _>::new(sender)
    );
    // TODO: Spawn some tasks
    //let _ = spawner;
    // let executor = EXECUTOR.init(Executor::new());

    // executor.run(|_spawner| {
    //     info!("Spawning tasks...");
    //     _spawner
    //         .spawn(broadcaster(sender))
    //         .expect("Failed to spawn broadcaster task");
    //     info!("Task spawned");
    // });
    let _ = _spawner.spawn(broadcaster(sender));
    let _ = _spawner.spawn(wifireporter(wifi_controller));

    // loop {
    //     info!("Hello world!");
    //     Timer::after(Duration::from_secs(1)).await;
    // }

    // for inspiration have a look at the examples at https://github.com/esp-rs/esp-hal/tree/esp-hal-v1.0.0-beta.0/examples/src/bin
}

#[embassy_executor::task]
async fn broadcaster(sender: &'static Mutex<NoopRawMutex, EspNowSender<'static>>) {
    loop {
        Timer::after(Duration::from_secs(1)).await;
        info!("Sending BROADCAST...");
        let mut sender = sender.lock().await;
        let status = sender.send_async(&BROADCAST_ADDRESS, b"Hello").await;
        info!("Send BROADCAST: {:?}", status);
    }
}

#[embassy_executor::task]
async fn wifireporter(wifi_controller: SharedWifiController<'static>) {
    loop {
        Timer::after(Duration::from_secs(1)).await;
        info!(
            "WIFI is connected?: {}",
            wifi_controller.lock().await.is_connected()
        );
    }
}