use shrev::{EventChannel, ReaderId};
use space_crush_common::misc::WorldHelper;
use specs::{prelude::*, ReadExpect, System, World, Write};

use crate::{
    misc::{KeyboardEvent, MouseEvent, WindowEvent},
    resources::InputState,
    Error,
};

pub struct StateUpdate {
    mouse_event_id: ReaderId<MouseEvent>,
    window_events_id: ReaderId<WindowEvent>,
    keyboard_events_id: ReaderId<KeyboardEvent>,
}

impl StateUpdate {
    pub fn new(world: &mut World) -> Result<Self, Error> {
        let mouse_event_id = world.register_event_reader::<MouseEvent>()?;
        let window_events_id = world.register_event_reader::<WindowEvent>()?;
        let keyboard_events_id = world.register_event_reader::<KeyboardEvent>()?;

        Ok(Self {
            mouse_event_id,
            window_events_id,
            keyboard_events_id,
        })
    }
}

#[derive(SystemData)]
pub struct StateUpdateData<'a> {
    input_state: Write<'a, InputState>,
    mouse_events: ReadExpect<'a, EventChannel<MouseEvent>>,
    window_events: ReadExpect<'a, EventChannel<WindowEvent>>,
    keyboard_events: ReadExpect<'a, EventChannel<KeyboardEvent>>,
}

impl<'a> System<'a> for StateUpdate {
    type SystemData = StateUpdateData<'a>;

    fn run(&mut self, data: Self::SystemData) {
        let StateUpdateData {
            mut input_state,
            mouse_events,
            window_events,
            keyboard_events,
        } = data;

        let events = mouse_events.read(&mut self.mouse_event_id);
        for event in events {
            match event {
                MouseEvent::Move(x, y) => {
                    let x = *x - input_state.resolution.0 as f32 / 2.0;
                    let y = input_state.resolution.1 as f32 / 2.0 - *y;

                    input_state.mouse_pos = (x, y);
                }
                MouseEvent::ButtonUp(button) => input_state.set_button_state(*button, false),
                MouseEvent::ButtonDown(button) => input_state.set_button_state(*button, true),
                _ => (),
            }
        }

        let events = window_events.read(&mut self.window_events_id);
        for event in events {
            match event {
                WindowEvent::Resize(w, h) => {
                    input_state.resolution = (*w, *h);
                }
                WindowEvent::Close => {
                    input_state.close_requested = true;
                }
            }
        }

        let events = keyboard_events.read(&mut self.keyboard_events_id);
        for event in events {
            match event {
                KeyboardEvent::KeyUp(code) => input_state.set_key_state(*code, false),
                KeyboardEvent::KeyDown(code) => input_state.set_key_state(*code, true),
                _ => (),
            }
        }
    }
}
