#![no_std]
#![no_main]

use core::{
    arch::asm,
    ops::{Add, Mul, Sub},
};

use fpga_lib::{
    interface::{Buttons, Leds},
    panic_handler,
    register::X31,
    vga::{BLACK, BLUE, Color, FONT_WIDTH, GREEN, HEIGHT, RED, VGA, WHITE, WIDTH},
};

type Float = f32;
pub const ITERATIONS: usize = 100;
pub const ASPECT_RATIO: f32 = WIDTH as f32 / HEIGHT as f32;

#[derive(Clone, Copy)]
struct Complex(Q3x29, Q3x29);

#[derive(Clone, Copy)]
struct Q3x29(i32);

impl From<f32> for Q3x29
{
    fn from(x: f32) -> Q3x29
    {
        const SCALE: f32 = 16.0;
        const MAX_Q28_4: f32 = (u32::MAX as f32) / SCALE;

        let clamped = x.clamp(0.0, MAX_Q28_4);
        let scaled = clamped * SCALE;

        Q3x29(scaled as i32)
    }
}

impl From<f64> for Q3x29
{
    fn from(x: f64) -> Q3x29
    {
        const SCALE: f64 = 16.0;
        const MAX_Q28_4: f64 = (u32::MAX as f64) / SCALE;

        let clamped = x.clamp(0.0, MAX_Q28_4);
        let scaled = clamped * SCALE;

        Q3x29(scaled as i32)
    }
}

impl Add for Q3x29
{
    type Output = Q3x29;

    fn add(self, rhs: Self) -> Self::Output
    {
        Q3x29(self.0 + rhs.0)
    }
}

impl Sub for Q3x29
{
    type Output = Q3x29;

    fn sub(self, rhs: Self) -> Self::Output
    {
        Q3x29(self.0 - rhs.0)
    }
}

impl Mul for Q3x29
{
    type Output = Q3x29;

    fn mul(self, rhs: Self) -> Self::Output
    {
        let prod: i64 = self.0 as i64 * rhs.0 as i64;
        Q3x29(((prod >> 28) & 0xFFFFFFFF) as i32)
    }
}
impl Complex
{
    pub fn sq_mag(&self) -> Q3x29
    {
        self.0 * self.0 + self.1 * self.1
    }
}

pub fn map(x: Float, xmin: Float, xmax: Float, ymin: Float, ymax: Float) -> Float
{
    ((x - xmin) / (xmax - xmin)) * (ymax - ymin) + ymin
}

impl Add for Complex
{
    type Output = Complex;

    fn add(self, Self(a, b): Self) -> Self::Output
    {
        Complex(self.0 + a, self.1 + b)
    }
}

impl Mul for Complex
{
    type Output = Complex;

    fn mul(self, Self(a, b): Self) -> Self::Output
    {
        Complex(self.0 * a - self.1 * b, self.0 * b + self.1 * a)
    }
}

panic_handler! {}

#[unsafe(no_mangle)]
pub extern "C" fn main() -> !
{
    let color_lut: [Color; ITERATIONS] = {
        let mut tab = [BLACK; _];
        for i in 0..(ITERATIONS - 1)
        {
            let val = ((i as Float / ITERATIONS as Float) * 255.) as u8;
            //tab[i] = Color::from_rgb(val, val, val);
            tab[i] = Color(val);
        }

        tab
    };

    let zoom_int = 1;
    let zoom = Q3x29::from(1. / zoom_int as f32);
    for x in 0..WIDTH
    {
        for y in 0..HEIGHT
        {
            // -0.761574,-0.0847596)
            let pos = Complex(
                Q3x29((x as i32 - WIDTH as i32 / 2) * (0x1FFFFFFF / WIDTH as i32)) * zoom
                    - Q3x29::from(0.761574),
                Q3x29((y as i32 - HEIGHT as i32 / 2) * (0x1FFFFFFF / WIDTH as i32)) * zoom
                    - Q3x29::from(0.0847596),
            );
            let mut acc = pos;
            let mut iter = 0;

            for i in 0..ITERATIONS
            {
                //acc = acc * acc + pos;
                acc = Complex(
                    pos.0 + acc.0 * acc.0 - acc.1 * acc.1,
                    pos.1 + Q3x29::from(2.) * acc.1 * acc.0,
                );
                if acc.sq_mag().0 > Q3x29::from(3.8).0
                {
                    iter = i;
                    break;
                }
            }

            unsafe {
                VGA::write_pixel_unsafe(x as u16, y as u16, color_lut[iter]);
            }
        }
    }

    loop
    {
        core::hint::spin_loop();
    }
}
