use crate::*;
use phosphor::*;
use std::collections::VecDeque;

const CONTROL:  u8 = 0x80;
const ALT:      u8 = 0x40;
const SHIFT:    u8 = 0x20;

const UP:       u8 = 0x80;
const DOWN:     u8 = 0x40;
const LEFT:     u8 = 0x20;
const RIGHT:    u8 = 0x10;
const CONFIRM:  u8 = 0x08;
const CANCEL:   u8 = 0x04;
const NEXT:     u8 = 0x02;
const PREVIOUS: u8 = 0x01;

macro_rules! test { ($value:expr, $mask:expr) => { $value & $mask != 0 }; }


pub struct InputDevice {
    pub wake_flag: bool,

    pub pointer_position: ScreenPosition,
    pub pointer_buttons: u8,
    pub pointer_active: bool,

    pub horizontal_scroll: i8,
    pub vertical_scroll: i8,
    pub horizontal_scroll_delta: f64,
    pub vertical_scroll_delta: f64,

    pub text_queue: VecDeque<u8>,
    pub modifiers: u8,
    pub navigation: u8,

    pub controller_1: u8,
    pub controller_2: u8,
    pub controller_3: u8,
    pub controller_4: u8,
}

impl InputDevice {
    pub fn new() -> Self {
        Self {
            wake_flag: false,

            pointer_position: ScreenPosition::ZERO,
            pointer_buttons: 0x00,
            pointer_active: false,

            horizontal_scroll: 0x00,
            vertical_scroll: 0x00,
            horizontal_scroll_delta: 0.0,
            vertical_scroll_delta: 0.0,

            text_queue: VecDeque::new(),
            modifiers: 0x00,
            navigation: 0x00,

            controller_1: 0x00,
            controller_2: 0x00,
            controller_3: 0x00,
            controller_4: 0x00,
        }
    }

    pub fn read_horizontal_scroll(&mut self) -> u8 {
        std::mem::take(&mut self.horizontal_scroll) as u8
    }

    pub fn read_vertical_scroll(&mut self) -> u8 {
        std::mem::take(&mut self.vertical_scroll) as u8
    }

    pub fn on_pointer_button(&mut self, mask: u8, action: Action) {
        let new_buttons = match action {
            Action::Pressed => self.pointer_buttons | mask,
            Action::Released => self.pointer_buttons & !mask,
        };
        if new_buttons != self.pointer_buttons {
            self.pointer_buttons = new_buttons;
            self.wake_flag = true;
        }
    }

    pub fn on_pointer_move(&mut self, position: ScreenPosition) {
        if position != self.pointer_position {
            self.pointer_position = position;
            self.wake_flag = true;
        }
    }

    pub fn on_scroll_horizontal(&mut self, delta: f64) {
        self.horizontal_scroll_delta += delta;
        while self.horizontal_scroll_delta >= 1.0 {
            self.horizontal_scroll = self.horizontal_scroll.saturating_add(1);
            self.horizontal_scroll_delta -= 1.0;
            self.wake_flag = true;
        }
        while self.horizontal_scroll_delta <= -1.0 {
            self.horizontal_scroll = self.horizontal_scroll.saturating_sub(1);
            self.horizontal_scroll_delta += 1.0;
            self.wake_flag = true;
        }
    }

    pub fn on_scroll_vertical(&mut self, delta: f64) {
        self.vertical_scroll_delta += delta;
        while self.vertical_scroll_delta >= 1.0 {
            self.vertical_scroll = self.vertical_scroll.saturating_add(1);
            self.vertical_scroll_delta -= 1.0;
            self.wake_flag = true;
        }
        while self.vertical_scroll_delta <= -1.0 {
            self.vertical_scroll = self.vertical_scroll.saturating_sub(1);
            self.vertical_scroll_delta += 1.0;
            self.wake_flag = true;
        }
    }

    pub fn on_character_input(&mut self, input: char) {
        if let Ok(byte) = u8::try_from(u32::from(input)) {
            self.text_queue.push_back(byte);
            self.wake_flag = true;
        }
    }

    pub fn on_keyboard_input(&mut self, input: KeyboardInput) {
        let mask = match input.key {
            KeyCode::Up     => UP,
            KeyCode::Down   => DOWN,
            KeyCode::Left   => LEFT,
            KeyCode::Right  => RIGHT,
            KeyCode::Return => CONFIRM,
            KeyCode::Escape => CANCEL,
            KeyCode::Tab => match test!(self.modifiers, SHIFT) {
                false => NEXT,
                true  => PREVIOUS
            },
            _ => return,
        };
        let navigation = match input.action {
            Action::Pressed => self.navigation | mask,
            Action::Released =>  self.navigation & !mask,
        };
        if navigation != self.navigation {
            self.navigation = navigation;
            self.wake_flag = true;
        }
    }

    pub fn on_modifier_change(&mut self, modifiers: ModifiersState) {
        let mut new_modifiers = 0x00;
        if modifiers.ctrl()  { new_modifiers |= CONTROL }
        if modifiers.alt()   { new_modifiers |= ALT     }
        if modifiers.shift() { new_modifiers |= SHIFT   }
        if new_modifiers != self.modifiers {
            self.modifiers = new_modifiers;
            self.wake_flag = true;
        }

    }
}