use crate::device;
use crate::ui;

pub const EVENT_TYPE_NONE: usize = 0;
pub const EVENT_TYPE_AI: usize = 1;
pub const EVENT_TYPE_SI: usize = 2;
pub const EVENT_TYPE_VI: usize = 3;
pub const EVENT_TYPE_PI: usize = 4;
pub const EVENT_TYPE_DP: usize = 5;
pub const EVENT_TYPE_SP: usize = 6;
pub const EVENT_TYPE_INT: usize = 7;
pub const EVENT_TYPE_SPDMA: usize = 8;
pub const EVENT_TYPE_COMPARE: usize = 9;
pub const EVENT_TYPE_VRU: usize = 10;
pub const EVENT_TYPE_PAK: usize = 11;
pub const EVENT_TYPE_SAVE: usize = 12;
pub const EVENT_TYPE_NMI: usize = 13;
pub const EVENT_TYPE_UPDATE_SCREEN: usize = 14;
pub const EVENT_TYPE_COUNT: usize = 15;

#[derive(PartialEq, Copy, Clone, serde::Serialize, serde::Deserialize)]
pub struct Event {
    pub enabled: bool,
    pub count: u64,
}

pub fn create_event(device: &mut device::Device, name: usize, when: u64) {
    device.cpu.events[name] = Event {
        enabled: true,
        count: device.cpu.cop0.regs[device::cop0::COP0_COUNT_REG] + when,
    };
    set_next_event(device);
}

pub fn create_event_at(device: &mut device::Device, name: usize, when: u64) {
    device.cpu.events[name] = Event {
        enabled: true,
        count: when,
    };
    set_next_event(device);
}

pub fn get_event(device: &device::Device, name: usize) -> Option<&Event> {
    if device.cpu.events[name].enabled {
        return Some(&device.cpu.events[name]);
    }
    None
}

pub fn remove_event(device: &mut device::Device, name: usize) {
    device.cpu.events[name].enabled = false;
}

fn get_event_handler(name: usize) -> fn(&mut device::Device) {
    match name {
        EVENT_TYPE_AI => device::ai::dma_event,
        EVENT_TYPE_SI => device::si::dma_event,
        EVENT_TYPE_VI => device::vi::vertical_interrupt_event,
        EVENT_TYPE_PI => device::pi::dma_event,
        EVENT_TYPE_DP => device::rdp::rdp_interrupt_event,
        EVENT_TYPE_SP => device::rsp_interface::rsp_event,
        EVENT_TYPE_INT => device::exceptions::interrupt_exception,
        EVENT_TYPE_SPDMA => device::rsp_interface::fifo_pop,
        EVENT_TYPE_COMPARE => device::cop0::compare_event,
        EVENT_TYPE_VRU => device::controller::vru::vru_talking_event,
        EVENT_TYPE_PAK => device::controller::pak_switch_event,
        EVENT_TYPE_SAVE => ui::storage::save_event,
        EVENT_TYPE_NMI => device::exceptions::reset_event,
        EVENT_TYPE_UPDATE_SCREEN => device::vi::update_screen,
        _ => dummy_event,
    }
}

pub fn trigger_event(device: &mut device::Device) {
    let next_event_name = device.cpu.next_event;
    device.cpu.events[next_event_name].enabled = false;

    let handler = get_event_handler(next_event_name);
    device.cpu.cop0.is_event = true;
    handler(device);
    device.cpu.cop0.is_event = false;
    set_next_event(device);
}

fn set_next_event(device: &mut device::Device) {
    device.cpu.next_event_count = u64::MAX;
    for (pos, i) in device.cpu.events.iter().enumerate() {
        if i.enabled && i.count < device.cpu.next_event_count {
            device.cpu.next_event_count = i.count;
            device.cpu.next_event = pos;
        }
    }
}

pub fn translate_events(device: &mut device::Device, old_count: u64, new_count: u64) {
    for i in device.cpu.events.iter_mut() {
        if i.enabled {
            i.count = i.count - old_count + new_count;
        }
    }
    set_next_event(device);
}

pub fn dummy_event(_device: &mut device::Device) {
    panic!("dummy event")
}
