use crate::critical;
use crate::task::{self, BlockingOn, Task};
use gba::prelude::*;
use gba_utils::gfx;

pub struct GameData {
    is_first_frame: bool,
    px: usize,
    py: usize,
    color: usize,
    bg_color: Color,
    prev_keystate: Keys,
    redraw_bg: bool,
}

static GAMEDATA: Mutex<GameData> = Mutex::new(GameData {
    is_first_frame: true,
    px: mode3::WIDTH / 2,
    py: mode3::HEIGHT / 2,
    color: 0,
    bg_color: Color::from_rgb(31, 31, 31),
    prev_keystate: Keys::new(),
    redraw_bg: false,
});

const COLORS: [Color; 7] = [
    Color(0x041F),
    Color(0x01FF),
    Color(0x03FF),
    Color(0x03E0),
    Color(0x7C00),
    Color(0x4009),
    Color(0x7C11),
];

pub fn init() -> ! {
    static mut DRAW_TASK: Task = Task::new(draw);
    static mut UPDATE_TASK: Task = Task::new(update);

    gfx::write_palette();
    gfx::write_sprite();
    gfx::enable_sprite();

    const SETTING: DisplayControl = DisplayControl::new()
        .with_display_mode(3)
        .with_display_bg0(false)
        .with_display_bg1(false)
        .with_display_bg2(true)
        .with_display_obj(true)
        .with_obj_vram_1d(true);
    DISPCNT.write(SETTING);

    let bg_setting = BG2CNT.read().with_priority(2);
    BG2CNT.write(bg_setting);

    const DISPLAY_SETTINGS: DisplayStatus = DisplayStatus::new()
        .with_vblank_irq_enabled(true)
        .with_vcount_irq_enabled(true)
        .with_vcount(0);

    DISPSTAT.write(DISPLAY_SETTINGS);

    const FLAGS: InterruptFlags = InterruptFlags::new().with_vblank(true).with_vcount(true);
    unsafe { IE.write(FLAGS) };
    unsafe { IME.write(true) };

    mode3::bitmap_xy(mode3::WIDTH / 2, mode3::HEIGHT / 2).write(Color::from_rgb(0, 0, 31));

    task::register(unsafe { &mut DRAW_TASK });
    task::register(unsafe { &mut UPDATE_TASK });

    task::start();
}

pub extern "C" fn draw() {
    loop {
        task::block_on(BlockingOn::VBlank);
        critical(|| {
            let mut gdata = GAMEDATA.lock();
            if gdata.redraw_bg || gdata.is_first_frame {
                gdata.is_first_frame = false;
                gdata.redraw_bg = false;
                let color32 = gdata.bg_color.0 as u32;
                let bulk_color = color32 << 16 | color32;
                unsafe {
                    CpuFastSet(&bulk_color, 0x600_0000 as *mut u32, 19200 | (1 << 24));
                }
            }

            if !gdata.redraw_bg {
                mode3::bitmap_xy(gdata.px, gdata.py).write(COLORS[gdata.color]);
                mode3::bitmap_xy(gdata.px + 1, gdata.py).write(COLORS[gdata.color]);
                mode3::bitmap_xy(gdata.px, gdata.py + 1).write(COLORS[gdata.color]);
                mode3::bitmap_xy(gdata.px + 1, gdata.py + 1).write(COLORS[gdata.color]);

                let mut oattr0 = OAM_ATTR0.index(0).read();
                let mut oattr1 = OAM_ATTR1.index(0).read();

                oattr0 = oattr0.with_y_pos(gdata.py.saturating_sub(2) as u16);
                oattr1 = oattr1.with_x_pos(gdata.px.saturating_sub(3) as u16);
                OAM_ATTR0.index(0).write(oattr0);
                OAM_ATTR1.index(0).write(oattr1);
            }
        });
    }
}

pub extern "C" fn update() {
    loop {
        task::block_on(BlockingOn::VCount);

        let this_frame_keys: Keys = KEYINPUT.read().into();

        critical(|| {
            let mut gdata = GAMEDATA.lock();
            gdata.px = gdata.px.wrapping_add(this_frame_keys.x_signum() as usize);
            gdata.py = gdata.py.wrapping_add(this_frame_keys.y_signum() as usize);

            if gdata.px >= mode3::WIDTH - 1 || gdata.py >= mode3::HEIGHT - 1 {
                gdata.px = mode3::WIDTH / 2;
                gdata.py = mode3::HEIGHT / 2;
                gdata.redraw_bg = true;
            }

            gdata.color = if this_frame_keys.l() {
                gdata.color + 1
            } else if this_frame_keys.r() {
                gdata.color.saturating_sub(1)
            } else {
                gdata.color
            } % COLORS.len();

            gdata.bg_color = if !this_frame_keys.a() && gdata.prev_keystate.a() {
                gdata.redraw_bg = true;
                Color(gdata.bg_color.0.rotate_left(6))
            } else if !this_frame_keys.b() && gdata.prev_keystate.b() {
                gdata.redraw_bg = true;
                Color(gdata.bg_color.0.rotate_right(6))
            } else {
                gdata.bg_color
            };

            if !this_frame_keys.select() && gdata.prev_keystate.select() {
                panic!("select? zie ik eruit als bol.com?");
            }

            gdata.prev_keystate = this_frame_keys;
        });
    }
}
