#![no_std]
#![no_main]

// TODO remove
#![allow(unused)]

extern crate panic_halt;

use core::fmt::Write;
use core::{
    arch::asm,
    ptr::{read_volatile, write_volatile},
};

use embedded_hal::prelude::{_embedded_hal_blocking_i2c_Read, _embedded_hal_blocking_i2c_Write};
use mcp4726::Status;
use riscv_rt::entry;
use smoltcp::wire::{IpAddress, Ipv4Address};
use smoltcp::{
    iface::{SocketSet, SocketStorage},
    time::Instant,
    wire::HardwareAddress,
};

mod eth;
mod i2c;
mod mcp4726;
mod uart;
mod litex_uart;
mod logging;

const MAC: [u8; 6] = [0xA0, 0xBB, 0xCC, 0xDD, 0xEE, 0xF0];

static mut SECONDS: u32 = 0;

/// External interrupt handler
#[export_name = "MachineExternal"]
fn external_interrupt_handler() {
    let cause = riscv::register::mcause::read();
    let mut uart = litex_uart::LiteXUart::new(0xf000_4000);
    writeln!(uart, "mcause: {}", cause.bits());

    if (cause.is_interrupt()) {
        let mut uart = litex_uart::LiteXUart::new(0xf000_4000);
        writeln!(uart, "mcause: {}", cause.code());

        if cause.code() == 1 {
            // TIMER0 event, we have reset so count another second
        }
    }
}

// use `main` as the entry point of this application
// `main` is not allowed to return
#[entry]
fn main() -> ! {
    //unsafe { eth::init(); }

    //let blink_period = unsafe {
    //    if eth::is_wishbone_correct() {
    //        10_000_000
    //    } else {
    //        500_000
    //    }
    //};
    let blink_period = 10_000_000u32;
    let mut uart = litex_uart::LiteXUart::new(0xf000_4000);
    writeln!(uart, "uart init");

    // enable timer

    let mut device = unsafe { eth::LiteEthDevice::try_init(0xf000_0800, 0x8000_0000).unwrap() };
    writeln!(uart, "eth init");

    use smoltcp::wire::{EthernetAddress, HardwareAddress};
    let mut config = smoltcp::iface::Config::default();
    config.hardware_addr = Some(HardwareAddress::Ethernet(EthernetAddress::from_bytes(&MAC)));

    let mut iface = smoltcp::iface::Interface::new(config, &mut device);
    // Set address
    iface.update_ip_addrs(|ip_addrs| {
        ip_addrs
            .push(smoltcp::wire::IpCidr::new(
                IpAddress::Ipv4(Ipv4Address::new(192, 168, 88, 69)),
                24,
            ))
            .unwrap();
    });

    iface
        .routes_mut()
        .add_default_ipv4_route(Ipv4Address::new(192, 168, 88, 1))
        .unwrap();

    // Create socket set with 4 available
    let mut socket_storage = [SocketStorage::EMPTY; 4];
    let mut socket_set = SocketSet::new(&mut socket_storage[..]);

    let mut last_blink: u32 = 0;
    let mut toggle = false;
    //defmt::info!("Done setup");

    unsafe {
        //riscv::interrupt::enable();
        //riscv::register::mie::set_mext();
        //riscv::register::mie::set_msoft();

        // Enable UART rx event for test
        //write_reg(0xf000_4014, 1u32);

        // Timer stuff
        write_reg(0xf000_3808, 0u32); // Disable timer
        write_reg(0xf000_3800, 0u32); // Set LOAD value
        write_reg(0xf000_3804, 60_000_000u32); // Set RELOAD value
        write_reg(0xf000_3808, 1u32); // Enable timer

        // Enable timer event
        //write_reg(0xf000_381c, 1u32);
    }

    loop {
        let now = millis();
        if now - last_blink > 1000 {
            last_blink = now;
            toggle = !toggle;
            write_led(if toggle { 1 } else { 0 });

            let val: u32 = unsafe {read_reg(0x8000_2000)};
            writeln!(uart, "Sampler value: 0x{:08x}", val).unwrap();
        }

        if iface.poll(Instant::from_millis(now), &mut device, &mut socket_set) {
        }

        handle_timer_event();
    }
}

fn handle_timer_event() {
    unsafe {
        if read_reg::<u32>(0xf000_3818) == 0 {
            return;
        }

        // Clear TIMER0 event status, and update time
        write_reg(0xf000_3818, 1u32);
        SECONDS += 1;
    }

}

fn busy_wait(ms: u32) {
    //let start = millis();
    //while millis() - start < ms {
    //    unsafe {
    //        asm!("nop");
    //    }
    //}

    for i in 0..ms*20_000 {
        unsafe {asm!("nop");}
    }
}

fn write_led(val: u32) {
    unsafe {
        write_reg(0xf000_2000, val);
    }
}

unsafe fn write_reg<T>(addr: u32, value: T) {
    write_volatile(addr as *mut T, value);
}

unsafe fn read_reg<T>(addr: u32) -> T {
    return read_volatile(addr as *mut T);
}

fn millis() -> u32 {
    riscv::interrupt::free(|| {
        unsafe {
            // Latch timer value
            write_reg(0xf000_380c, 1u32);
            // Read timer value
            let val: u32 = read_reg(0xf000_3810);
            let val = 60_000_000 - val;
            (SECONDS * 1000) + val / 60_000
        }
    })
}
