#![no_std]
#![no_main]

use core::ops::{Add, Mul};

use fpga_lib::{
    interface::Buttons,
    panic_handler,
    vga::{Color, BLACK, HEIGHT, VGA, WIDTH},
};

type Float = f32;
pub const ITERATIONS_SHOWN: usize = 300;
pub const ASPECT_RATIO: f32 = WIDTH as f32 / HEIGHT as f32;

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

impl Complex {
    pub fn sq_mag(&self) -> f64 {
        self.0 * self.0 + self.1 * self.1
    }
}

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 mut iterations: usize = 300;
    let color_lut: [Color; ITERATIONS_SHOWN] = {
        let mut tab = [BLACK; _];
        (0..(ITERATIONS_SHOWN - 1)).for_each(|i| {
            let val = ((i as Float / ITERATIONS_SHOWN as Float) * 255.) as u8;
            tab[i] = Color(val);
        });

        tab
    };

    let mut zoom_int = 60;
    const DELTA: f64 = 0.1;
    let mut current_pos = Complex(1.002_674_971_219_186_5, 0.296_764_969_447_543_66);

    loop {
        let zoom = 1. / zoom_int as f64;
        for y in 0..HEIGHT {
            if unsafe { Buttons::is_button_pressed() } {
                break;
            }
            for x in 0..WIDTH {
                // -0.761574,-0.0847596
                let pos = Complex(
                    ((x as i64 - WIDTH as i64 / 2) as f64 * 5. / WIDTH as f64) * zoom
                        - current_pos.0,
                    ((y as i64 - HEIGHT as i64 / 2) as f64 * 5. / WIDTH as f64) * zoom
                        - current_pos.1,
                );
                let mut acc = pos;
                let mut iter = 0;

                let p = (pos.0 - 1. / 4.) * (pos.0 - 1. / 4.) + pos.1 * pos.1;
                if pos.0 < libm::sqrt(p) - 2. * p + 1. / 4.
                    || (pos.0 + 1.) * (pos.0 + 1.) + pos.1 * pos.1 < 1. / 16.
                {
                    unsafe { VGA::write_pixel_unsafe(x as u16, y as u16, BLACK) };
                    continue;
                }

                for i in 0..iterations {
                    acc = Complex(
                        pos.0 + acc.0 * acc.0 - acc.1 * acc.1,
                        pos.1 + acc.1 * acc.0 + acc.1 * acc.0,
                    );
                    if acc.sq_mag() > 4. {
                        iter = i;
                        break;
                    }
                }

                unsafe {
                    VGA::write_pixel_unsafe(
                        x as u16,
                        y as u16,
                        color_lut
                            [(iter as f32 / iterations as f32 * ITERATIONS_SHOWN as f32) as usize],
                    );
                }
            }
        }
        'outer: loop {
            for direction in 0..7 {
                let button = unsafe { fpga_lib::interface::Buttons::get_button(direction as u8) };
                if button {
                    match direction {
                        0 => current_pos.0 += DELTA * zoom,
                        1 => current_pos.0 -= DELTA * zoom,
                        2 => current_pos.1 += DELTA * zoom,
                        3 => current_pos.1 -= DELTA * zoom,
                        4 => zoom_int += 1,
                        5 => zoom_int -= 1,
                        6 => iterations += 50,
                        _ => {}
                    }

                    while unsafe { fpga_lib::interface::Buttons::get_button(direction as u8) } {}
                    break 'outer;
                }
            }
        }
    }
}
