use crate::system::*;

use std::sync::RwLock;
use windows::Win32::{
    Foundation::{
        HWND,
        LPARAM,
        LRESULT,
        WPARAM,
    },
    UI::{
        Input::{
            GetRawInputData,
            HRAWINPUT,
            RAWINPUT,
            RAWINPUTDEVICE,
            RAWINPUTHEADER,
            RID_INPUT,
            RIDEV_DEVNOTIFY,
            RIM_TYPEHID, RIM_TYPEKEYBOARD, RIM_TYPEMOUSE,
        },
        WindowsAndMessaging::{
            DefWindowProcW,
            MSG,
            WM_MOVE, WM_SIZE, WM_CLOSE,
            WM_CAPTURECHANGED,

            WM_INPUT, WM_INPUT_DEVICE_CHANGE,
            WM_DEVICECHANGE, WM_DISPLAYCHANGE,
            WM_NULL,
        },
    },
    System::Threading::GetCurrentThreadId,
};

use super::InternalQuery;

pub static mut WINPROC_SENDER :RwLock<Option<(
    crossbeam::channel::Sender<Query>,
    crossbeam::channel::Sender<InternalQuery>
)>> = RwLock::new(None);

pub unsafe extern "system" fn window_proc(
    hwnd:HWND,
    msg:u32,
    wparam:WPARAM,
    lparam:LPARAM,
) -> LRESULT {
    if let Ok(sender) = WINPROC_SENDER.read() {
        if let Some((notify, sender)) = sender.as_ref() {
            match msg {
                WM_MOVE => {
                    if sender.send(InternalQuery::Move {
                        handle:hwnd.into(),
                    }).is_ok() {
                        notify.send(Query::new(None, QueryData::Notify)).ok();
                    }
                }

                WM_SIZE => {
                    let width = lparam.0 as i32;
                    let height = (lparam.0 >> 32) as i32;

                    if sender.send(InternalQuery::Resize {
                        handle:hwnd.into(), width, height,
                    }).is_ok() {
                        notify.send(Query::new(None, QueryData::Notify)).ok();
                    }
                }

                WM_CLOSE => {
                    if sender.send(InternalQuery::Close {
                        handle:hwnd.into(),
                    }).is_ok() {
                        notify.send(Query::new(None, QueryData::Notify)).ok();
                    }
                }


                WM_CAPTURECHANGED => {

                }


                WM_INPUT => {
                    let mut _rawinput_header = RAWINPUTHEADER::default();
                    let header_size = std::mem::size_of::<RAWINPUTHEADER>() as u32;

                    let mut size = 0;
                    let result = unsafe {GetRawInputData(
                        HRAWINPUT(lparam.0 as _),
                        RID_INPUT,
                        None,
                        &mut size,
                        header_size,
                    )};

                    if result == 0 && size > 0 {
                        let mut buffer = vec![0u8; size as usize];

                        if unsafe {GetRawInputData(
                            HRAWINPUT(lparam.0 as _),
                            RID_INPUT,
                            Some(buffer.as_mut_ptr() as *mut _),
                            &mut size,
                            header_size,
                        )} > 0 {
                            let raw_input :&RAWINPUT = &*(buffer.as_ptr() as *const RAWINPUT);

                            let resp = InternalQuery::Input {
                                handle:hwnd.into(),
                            };

                            const TYPE_MOUSE :u32 = RIM_TYPEMOUSE.0;
                            const TYPE_KEYBOARD :u32 = RIM_TYPEKEYBOARD.0;
                            const TYPE_HID :u32 = RIM_TYPEHID.0;
                            match raw_input.header.dwType {
                                TYPE_MOUSE => {
                                    let _data = unsafe {raw_input.data.mouse};
                                    //sender.send(0).ok();
                                }

                                TYPE_KEYBOARD => {
                                    let _data = unsafe {raw_input.data.keyboard};
                                }

                                TYPE_HID => {
                                    let _data = unsafe {raw_input.data.hid};
                                }

                                _ => { }
                            }

                            if sender.send(resp).is_ok() {
                                notify.send(Query::new(None, QueryData::Notify)).ok();
                            }
                        }
                    }

                    return LRESULT(0);
                }

                WM_INPUT_DEVICE_CHANGE => {
                    let state = wparam.0 == 1;
                    let handle = lparam.0 as usize;

                    if sender.send(InternalQuery::InputDeviceChange {
                        state,
                        handle,
                    }).is_ok() {
                        notify.send(Query::new(None, QueryData::Notify)).ok();
                    }
                }

                WM_DEVICECHANGE | WM_DISPLAYCHANGE => {
                    if sender.send(InternalQuery::DeviceChange).is_ok() {
                        notify.send(Query::new(None, QueryData::Notify)).ok();
                    }
                }

                _ => { }
            }
        }
    }
    DefWindowProcW(hwnd, msg, wparam, lparam)
}
