// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved.
//
// StratoVirt is licensed under Mulan PSL v2.
// You can use this software according to the terms and conditions of the Mulan
// PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
//         http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY
// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
// See the Mulan PSL v2 for more details.

use std::{
    collections::{HashMap, HashSet},
    sync::{Arc, Mutex},
};

use anyhow::Result;
use log::debug;
use once_cell::sync::Lazy;

use util::bitmap::Bitmap;

// Logical window size for mouse.
pub const ABS_MAX: u64 = 0x7fff;
// Event type of Point.
pub const INPUT_POINT_LEFT: u32 = 0x01;
pub const INPUT_POINT_RIGHT: u32 = 0x02;
pub const INPUT_POINT_MIDDLE: u32 = 0x04;
pub const INPUT_POINT_BACK: u32 = 0x08;
pub const INPUT_POINT_FORWARD: u32 = 0x10;
pub const INPUT_BUTTON_WHEEL_UP: u32 = 0x20;
pub const INPUT_BUTTON_WHEEL_DOWN: u32 = 0x40;
pub const INPUT_BUTTON_WHEEL_LEFT: u32 = 0x80;
pub const INPUT_BUTTON_WHEEL_RIGHT: u32 = 0x100;
pub const INPUT_BUTTON_MASK: u32 = 0x1f;

// ASCII value.
pub const ASCII_A: i32 = 65;
pub const ASCII_Z: i32 = 90;
pub const UPPERCASE_TO_LOWERCASE: i32 = 32;
const ASCII_A_LOWERCASE: i32 = 97;
const ASCII_Z_LOWERCASE: i32 = 122;
const BIT_PER_BYTE: u32 = 8;

// Keycode.
pub const KEYCODE_1: u16 = 2;
pub const KEYCODE_9: u16 = 10;
const KEYCODE_CTRL: u16 = 29;
pub const KEYCODE_RET: u16 = 38;
const KEYCODE_SHIFT: u16 = 42;
const KEYCODE_SHIFT_R: u16 = 54;
const KEYCODE_ALT: u16 = 56;
pub const KEYCODE_CAPS_LOCK: u16 = 58;
pub const KEYCODE_NUM_LOCK: u16 = 69;
pub const KEYCODE_SCR_LOCK: u16 = 70;
const KEYCODE_CTRL_R: u16 = 157;
const KEYCODE_ALT_R: u16 = 184;
const KEYPAD_1: u16 = 0xffb0;
const KEYPAD_9: u16 = 0xffb9;
const KEYPAD_SEPARATOR: u16 = 0xffac;
const KEYPAD_DECIMAL: u16 = 0xffae;
const KEYCODE_KP_7: u16 = 0x47;
const KEYCODE_KP_DECIMAL: u16 = 0x53;
// Led (HID)
pub const NUM_LOCK_LED: u8 = 0x1;
pub const CAPS_LOCK_LED: u8 = 0x2;
pub const SCROLL_LOCK_LED: u8 = 0x4;

static INPUTS: Lazy<Arc<Mutex<Inputs>>> = Lazy::new(|| Arc::new(Mutex::new(Inputs::default())));

static LED_STATE: Lazy<Arc<Mutex<LedState>>> =
    Lazy::new(|| Arc::new(Mutex::new(LedState::default())));

#[derive(Debug)]
pub enum InputType {
    KeyEvent,
    MoveEvent,
    ButtonEvent,
}

#[derive(Default)]
pub enum Axis {
    #[default]
    X,
    Y,
}

#[derive(Default)]
pub struct MoveEvent {
    pub axis: Axis,
    pub data: u32,
}

#[derive(Default)]
pub struct ButtonEvent {
    pub button: u32,
    pub down: bool,
}

#[derive(Default)]
pub struct KeyEvent {
    pub keycode: u16,
    pub down: bool,
}

pub struct InputEvent {
    pub input_type: InputType,
    pub move_event: MoveEvent,
    pub button_event: ButtonEvent,
    pub key_event: KeyEvent,
}

impl InputEvent {
    fn new(input_type: InputType) -> Self {
        Self {
            input_type,
            move_event: MoveEvent::default(),
            button_event: ButtonEvent::default(),
            key_event: KeyEvent::default(),
        }
    }
}

// Keyboard Modifier State
pub enum KeyboardModifier {
    KeyModNone = 0,
    KeyModShift = 1,
    KeyModCtrl = 2,
    KeyModAlt = 3,
    KeyModAltgr = 4,
    KeyModNumlock = 5,
    KeyModCapslock = 6,
    KeyModMax = 7,
}

/// Record the keyboard status,
/// Including the press information of keys,
/// and some status information.
pub struct KeyBoardState {
    /// Keyboard state.
    pub keystate: HashSet<u16>,
    /// Key Modifier states.
    pub keymods: Bitmap<u8>,
}

impl Default for KeyBoardState {
    fn default() -> Self {
        Self {
            keystate: HashSet::new(),
            keymods: Bitmap::new(
                KeyboardModifier::KeyModMax as usize / (BIT_PER_BYTE as usize) + 1,
            ),
        }
    }
}

impl KeyBoardState {
    /// Get the corresponding keyboard modifier.
    fn keyboard_modifier_get(&self, key_mod: KeyboardModifier) -> bool {
        match self.keymods.contain(key_mod as usize) {
            Ok(res) => res,
            Err(_e) => false,
        }
    }

    /// Reset all keyboard modifier state.
    fn keyboard_state_reset(&mut self) {
        self.keymods.clear_all();
    }

    /// Record the press and up state in the keyboard.
    fn keyboard_state_update(&mut self, keycode: u16, down: bool) -> Result<()> {
        // Key is not pressed and the incoming key action is up.
        if !down && !self.keystate.contains(&keycode) {
            return Ok(());
        }

        // Update Keyboard key modifier state.
        if down {
            self.keystate.insert(keycode);
        } else {
            self.keystate.remove(&keycode);
        }

        // Update Keyboard modifier state.
        match keycode {
            KEYCODE_SHIFT | KEYCODE_SHIFT_R => {
                self.keyboard_modstate_update(
                    KEYCODE_SHIFT,
                    KEYCODE_SHIFT_R,
                    KeyboardModifier::KeyModShift,
                )?;
            }
            KEYCODE_CTRL | KEYCODE_CTRL_R => {
                self.keyboard_modstate_update(
                    KEYCODE_CTRL,
                    KEYCODE_CTRL_R,
                    KeyboardModifier::KeyModCtrl,
                )?;
            }
            KEYCODE_ALT => {
                self.keyboard_modstate_update(
                    KEYCODE_ALT,
                    KEYCODE_ALT,
                    KeyboardModifier::KeyModAlt,
                )?;
            }
            KEYCODE_ALT_R => {
                self.keyboard_modstate_update(
                    KEYCODE_ALT_R,
                    KEYCODE_ALT_R,
                    KeyboardModifier::KeyModAltgr,
                )?;
            }
            KEYCODE_CAPS_LOCK => {
                if down {
                    self.keymods
                        .change(KeyboardModifier::KeyModCapslock as usize)?;
                }
            }
            KEYCODE_NUM_LOCK => {
                if down {
                    self.keymods
                        .change(KeyboardModifier::KeyModNumlock as usize)?;
                }
            }
            _ => {}
        }

        Ok(())
    }

    /// If one of the keys keycode_1 and keycode_2 is pressed,
    /// Then the corresponding keyboard modifier state will be set.
    /// Otherwise, it will be clear.
    fn keyboard_modstate_update(
        &mut self,
        keycode_1: u16,
        keycode_2: u16,
        mod_state: KeyboardModifier,
    ) -> Result<()> {
        if self.keystate.contains(&keycode_1) | self.keystate.contains(&keycode_2) {
            self.keymods.set(mod_state as usize)?;
        } else {
            self.keymods.clear(mod_state as usize)?;
        }
        Ok(())
    }
}

#[derive(Default)]
struct LedState {
    kbd_led: u8,
}

#[derive(Default)]
struct Inputs {
    kbd_ids: Vec<String>,
    kbd_lists: HashMap<String, Arc<Mutex<dyn KeyboardOpts>>>,
    tablet_ids: Vec<String>,
    tablet_lists: HashMap<String, Arc<Mutex<dyn PointerOpts>>>,
    keyboard_state: KeyBoardState,
}

impl Inputs {
    fn register_kbd(&mut self, device: &str, kbd: Arc<Mutex<dyn KeyboardOpts>>) {
        self.kbd_ids.insert(0, device.to_string());
        self.kbd_lists.insert(device.to_string(), kbd);
    }

    fn unregister_kbd(&mut self, device: &str) {
        self.kbd_lists.remove(&device.to_string());
        let len = self.kbd_ids.len();
        for i in 0..len {
            if self.kbd_ids[i] == device {
                self.kbd_ids.remove(i);
                break;
            }
        }
    }

    fn register_mouse(&mut self, device: &str, tablet: Arc<Mutex<dyn PointerOpts>>) {
        self.tablet_ids.insert(0, device.to_string());
        self.tablet_lists.insert(device.to_string(), tablet);
    }

    fn unregister_mouse(&mut self, device: &str) {
        self.tablet_lists.remove(&device.to_string());
        let len = self.tablet_ids.len();
        for i in 0..len {
            if self.tablet_ids[i] == device {
                self.tablet_ids.remove(i);
                break;
            }
        }
    }

    fn get_active_kbd(&mut self) -> Option<Arc<Mutex<dyn KeyboardOpts>>> {
        if !self.kbd_ids.is_empty() {
            let kbd = self.kbd_lists.get(&self.kbd_ids[0])?.clone();
            Some(kbd)
        } else {
            None
        }
    }

    fn get_active_mouse(&mut self) -> Option<Arc<Mutex<dyn PointerOpts>>> {
        if !self.tablet_ids.is_empty() {
            let mouse = self.tablet_lists.get(&self.tablet_ids[0])?.clone();
            Some(mouse)
        } else {
            None
        }
    }

    fn press_key(&mut self, keycode: u16) -> Result<()> {
        self.keyboard_state.keyboard_state_update(keycode, true)?;
        let kbd = self.get_active_kbd();
        if let Some(k) = kbd.as_ref() {
            k.lock().unwrap().do_key_event(keycode, true)?;
        }
        self.keyboard_state.keyboard_state_update(keycode, false)?;
        if let Some(k) = kbd.as_ref() {
            k.lock().unwrap().do_key_event(keycode, false)?;
        }
        Ok(())
    }
}

pub fn register_keyboard(device: &str, kbd: Arc<Mutex<dyn KeyboardOpts>>) {
    INPUTS.lock().unwrap().register_kbd(device, kbd);
}

pub fn unregister_keyboard(device: &str) {
    INPUTS.lock().unwrap().unregister_kbd(device);
}

pub fn register_pointer(device: &str, tablet: Arc<Mutex<dyn PointerOpts>>) {
    INPUTS.lock().unwrap().register_mouse(device, tablet);
}

pub fn unregister_pointer(device: &str) {
    INPUTS.lock().unwrap().unregister_mouse(device);
}

pub fn input_move_abs(axis: Axis, data: u32) -> Result<()> {
    let mut input_event = InputEvent::new(InputType::MoveEvent);
    let move_event = MoveEvent { axis, data };
    input_event.move_event = move_event;

    let mouse = INPUTS.lock().unwrap().get_active_mouse();
    if let Some(m) = mouse {
        m.lock().unwrap().update_point_state(input_event)?;
    }

    Ok(())
}

pub fn input_button(button: u32, down: bool) -> Result<()> {
    let mut input_event = InputEvent::new(InputType::ButtonEvent);
    let button_event = ButtonEvent { button, down };
    input_event.button_event = button_event;

    let mouse = INPUTS.lock().unwrap().get_active_mouse();
    if let Some(m) = mouse {
        m.lock().unwrap().update_point_state(input_event)?;
    }

    Ok(())
}

pub fn input_point_sync() -> Result<()> {
    let mouse = INPUTS.lock().unwrap().get_active_mouse();
    if let Some(m) = mouse {
        m.lock().unwrap().sync()?;
    }
    Ok(())
}

pub fn key_event(keycode: u16, down: bool) -> Result<()> {
    let kbd = INPUTS.lock().unwrap().get_active_kbd();
    if let Some(k) = kbd {
        k.lock().unwrap().do_key_event(keycode, down)?;
    }
    Ok(())
}

pub fn trigger_key(keycode: u16) -> Result<()> {
    key_event(keycode, true)?;
    key_event(keycode, false)
}

/// A complete mouse click event.
pub fn press_mouse(button: u32) -> Result<()> {
    input_button(button, true)?;
    input_point_sync()?;
    input_button(button, false)?;
    input_point_sync()
}

/// 1. Keep the key state in keyboard_state.
/// 2. Sync the caps lock and num lock state to guest.
pub fn update_key_state(down: bool, keysym: i32, keycode: u16) -> Result<()> {
    let mut locked_input = INPUTS.lock().unwrap();
    let upper = (ASCII_A..=ASCII_Z).contains(&keysym);
    let is_letter = upper || (ASCII_A_LOWERCASE..=ASCII_Z_LOWERCASE).contains(&keysym);
    let in_keypad = (KEYCODE_KP_7..=KEYCODE_KP_DECIMAL).contains(&keycode);

    if down && is_letter {
        let shift = locked_input
            .keyboard_state
            .keyboard_modifier_get(KeyboardModifier::KeyModShift);
        let in_upper = check_kbd_led_state(CAPS_LOCK_LED);
        if (shift && upper == in_upper) || (!shift && upper != in_upper) {
            debug!("Correct caps lock {} inside {}", upper, in_upper);
            locked_input.press_key(KEYCODE_CAPS_LOCK)?;
        }
    } else if down && in_keypad {
        let numlock = keysym_is_num_lock(keysym);
        let in_numlock = check_kbd_led_state(NUM_LOCK_LED);
        if in_numlock != numlock {
            debug!("Correct num lock {} inside {}", numlock, in_numlock);
            locked_input.press_key(KEYCODE_NUM_LOCK)?;
        }
    }

    locked_input
        .keyboard_state
        .keyboard_state_update(keycode, down)
}

pub fn keyboard_update(down: bool, keycode: u16) -> Result<()> {
    let mut locked_input = INPUTS.lock().unwrap();
    locked_input
        .keyboard_state
        .keyboard_state_update(keycode, down)
}

/// Release all pressed key.
pub fn release_all_key() -> Result<()> {
    let mut locked_input = INPUTS.lock().unwrap();
    let mut keycode_lists: Vec<u16> = Vec::new();
    for keycode in locked_input.keyboard_state.keystate.iter() {
        keycode_lists.push(*keycode);
    }
    for keycode in keycode_lists.iter() {
        locked_input
            .keyboard_state
            .keyboard_state_update(*keycode, false)?;
        if let Some(k) = locked_input.get_active_kbd().as_ref() {
            k.lock().unwrap().do_key_event(*keycode, false)?;
        }
    }
    Ok(())
}

pub fn check_kbd_led_state(state: u8) -> bool {
    LED_STATE.lock().unwrap().kbd_led & state == state
}

pub fn get_kbd_led_state() -> u8 {
    LED_STATE.lock().unwrap().kbd_led
}

pub fn set_kbd_led_state(state: u8) {
    LED_STATE.lock().unwrap().kbd_led = state;
}

pub fn keyboard_modifier_get(key_mod: KeyboardModifier) -> bool {
    INPUTS
        .lock()
        .unwrap()
        .keyboard_state
        .keyboard_modifier_get(key_mod)
}

pub fn keyboard_state_reset() {
    INPUTS.lock().unwrap().keyboard_state.keyboard_state_reset();
}

fn keysym_is_num_lock(sym: i32) -> bool {
    matches!(
        (sym & 0xffff) as u16,
        KEYPAD_1..=KEYPAD_9 | KEYPAD_SEPARATOR | KEYPAD_DECIMAL
    )
}

pub trait KeyboardOpts: Send {
    fn do_key_event(&mut self, keycode: u16, down: bool) -> Result<()>;
}

pub trait PointerOpts: Send {
    fn update_point_state(&mut self, input_event: InputEvent) -> Result<()>;
    fn sync(&mut self) -> Result<()>;
}

#[cfg(test)]
mod tests {
    use anyhow::bail;

    #[cfg(feature = "keycode")]
    use crate::keycode::{DpyMod, KeyCode};
    static TEST_INPUT: Lazy<Arc<Mutex<TestInput>>> =
        Lazy::new(|| Arc::new(Mutex::new(TestInput::default())));

    use super::*;

    pub struct TestInput {
        kbd: Arc<Mutex<TestKbd>>,
        tablet: Arc<Mutex<TestTablet>>,
    }

    impl Default for TestInput {
        fn default() -> Self {
            Self {
                kbd: Arc::new(Mutex::new(TestKbd {
                    keycode: 0,
                    down: false,
                })),
                tablet: Arc::new(Mutex::new(TestTablet {
                    button: 0,
                    x: 0,
                    y: 0,
                })),
            }
        }
    }

    impl TestInput {
        fn register_input(&self) {
            register_keyboard("TestKeyboard", self.kbd.clone());
            register_pointer("TestPointer", self.tablet.clone());
        }

        fn unregister_input(&self) {
            unregister_keyboard("TestKeyboard");
            unregister_pointer("TestPointer");
            self.kbd.lock().unwrap().keycode = 0;
            self.kbd.lock().unwrap().down = false;
            self.tablet.lock().unwrap().x = 0;
            self.tablet.lock().unwrap().y = 0;
            self.tablet.lock().unwrap().button = 0;
        }
    }

    #[derive(Default)]
    pub struct TestKbd {
        keycode: u16,
        down: bool,
    }

    impl KeyboardOpts for TestKbd {
        fn do_key_event(&mut self, keycode: u16, down: bool) -> Result<()> {
            self.keycode = keycode;
            self.down = down;
            Ok(())
        }
    }

    #[derive(Default)]
    pub struct TestTablet {
        pub button: u32,
        x: u32,
        y: u32,
    }

    impl PointerOpts for TestTablet {
        fn update_point_state(&mut self, input_event: InputEvent) -> Result<()> {
            match input_event.input_type {
                InputType::MoveEvent => match input_event.move_event.axis {
                    Axis::X => self.x = input_event.move_event.data,
                    Axis::Y => self.y = input_event.move_event.data,
                },
                InputType::ButtonEvent => {
                    if input_event.button_event.down {
                        self.button |= input_event.button_event.button;
                    } else {
                        self.button &= !(input_event.button_event.button & 0x7);
                    }
                }
                _ => bail!("Input type: {:?} is unsupported", input_event.input_type),
            }
            Ok(())
        }

        fn sync(&mut self) -> Result<()> {
            Ok(())
        }
    }

    #[test]
    fn test_input_basic() {
        let test_input = TEST_INPUT.lock().unwrap();
        test_input.register_input();
        let test_kdb = test_input.kbd.clone();
        let test_mouse = test_input.tablet.clone();

        assert!(key_event(12, true).is_ok());
        assert_eq!(test_kdb.lock().unwrap().keycode, 12);
        assert_eq!(test_kdb.lock().unwrap().down, true);

        // Test point event.
        assert_eq!(test_mouse.lock().unwrap().button, 0);
        assert_eq!(test_mouse.lock().unwrap().x, 0);
        assert_eq!(test_mouse.lock().unwrap().y, 0);
        register_pointer("TestPointer", test_mouse.clone());

        assert!(input_move_abs(Axis::X, 54).is_ok());
        assert!(input_move_abs(Axis::Y, 12).is_ok());
        assert!(input_button(1, true).is_ok());
        assert!(input_point_sync().is_ok());

        assert_eq!(test_mouse.lock().unwrap().button, 1);
        assert_eq!(test_mouse.lock().unwrap().x, 54);
        assert_eq!(test_mouse.lock().unwrap().y, 12);

        test_input.unregister_input();
    }

    #[cfg(feature = "keycode")]
    #[test]
    fn test_release_all_key() {
        fn do_key_event(press: bool, keysym: i32, keycode: u16) -> Result<()> {
            update_key_state(press, keysym, keycode)?;
            key_event(keycode, press)
        }

        // Test keyboard event.
        let test_input = TEST_INPUT.lock().unwrap();
        test_input.register_input();
        let test_kdb = test_input.kbd.clone();

        #[cfg(not(all(target_env = "ohos", feature = "ohui_srv")))]
        let keysym2qkeycode = KeyCode::keysym_to_qkeycode(DpyMod::Gtk);
        #[cfg(all(target_env = "ohos", feature = "ohui_srv"))]
        let keysym2qkeycode = KeyCode::keysym_to_qkeycode(DpyMod::Ohui);
        // ["0", "a", "space"]
        #[cfg(not(all(target_env = "ohos", feature = "ohui_srv")))]
        let keysym_lists: Vec<u16> = vec![0x0030, 0x0061, 0x0020];
        #[cfg(all(target_env = "ohos", feature = "ohui_srv"))]
        let keysym_lists: Vec<u16> = vec![0x07D0, 0x07E1, 0x0802];
        let keycode_lists: Vec<u16> = keysym_lists
            .iter()
            .map(|x| *keysym2qkeycode.get(&x).unwrap())
            .collect();
        for idx in 0..keysym_lists.len() {
            let keysym = keycode_lists[idx];
            let keycode = keycode_lists[idx];
            assert!(do_key_event(true, keysym as i32, keycode).is_ok());
            assert_eq!(test_kdb.lock().unwrap().keycode, keycode);
            assert_eq!(test_kdb.lock().unwrap().down, true);
        }

        let locked_input = INPUTS.lock().unwrap();
        for keycode in &keycode_lists {
            assert!(locked_input.keyboard_state.keystate.contains(keycode));
            assert!(locked_input.keyboard_state.keystate.contains(keycode));
        }
        drop(locked_input);

        // Release all keys
        assert!(release_all_key().is_ok());

        let locked_input = INPUTS.lock().unwrap();
        for keycode in &keycode_lists {
            assert!(!locked_input.keyboard_state.keystate.contains(keycode));
            assert!(!locked_input.keyboard_state.keystate.contains(keycode));
        }
        drop(locked_input);

        test_input.unregister_input();
    }
}