///
#[derive(Debug, Clone, Copy)]
pub struct Point {
    ///
    pub x: i32,
    ///
    pub y: i32,
}
impl From<(i32, i32)> for Point {
    fn from(pos: (i32, i32)) -> Self {
        Point { x: pos.0, y: pos.1 }
    }
}
/// ## control mouse
/// ```rust
///fn main()->doe::DynError<()> {
///    use std::time::Duration;
///    use doe::mouse::press;
///    move_to(300, 700);
///    press("left");
///    move_to_with_duration(0, 0,Duration::from_secs(5));
///    move_and_press_right(800,900);
///    press("right");
///    Ok(())
///}
///```
#[allow(warnings)]
#[cfg(feature = "mouse")]
pub mod mouse {
    pub mod common {
        use super::super::Point;
        use mouse_rs::{types::keys::Keys, Mouse};
        use std::thread::sleep;
        use std::time::Duration;
        ///```
        ///fn main() {
        ///    use doe::mouse;
        ///    mouse::scrool(-2);//scrool down
        ///    mouse::scrool(2);//scrool up
        ///}
        /// ```
        pub fn scrool(delta: i32) {
            let mouse = Mouse::new();
            mouse.wheel(delta);
        }

        ///move_to_select_all_and_copy_all_then_get_clipboard
        /// x is from left to right
        ///
        /// y is from top to bottom
        #[cfg(target_os = "windows")]
        #[cfg(feature = "clip")]
        pub fn move_to_select_all_and_copy_all_then_get_clipboard(
            x: i32,
            y: i32,
        ) -> Result<String, Box<dyn std::error::Error>> {
            use crate::*;
            mouse::move_to_press_left(x, y);
            use crate::keyboard::KeyCode;
            let ctrl = if cfg!(target_os = "windows") {
                KeyCode::CONTROL
            } else {
                KeyCode::CONTROL
            };
            let a = if cfg!(target_os = "windows") {
                KeyCode::A
            } else {
                KeyCode::A
            };

            let c = if cfg!(target_os = "windows") {
                KeyCode::C
            } else {
                KeyCode::C
            };

            //ctrl+A select all
            keyboard::key_press(ctrl);
            keyboard::key_press(a).sleep(1.0);
            keyboard::key_release(ctrl);
            keyboard::key_release(a);

            //ctrl+C copy all
            keyboard::key_press(ctrl);
            keyboard::key_press(c).sleep(1.0);
            keyboard::key_release(ctrl);
            keyboard::key_release(c);

            clipboard::get_clipboard()
        }
        /// x is from left to right
        ///
        /// y is from top to bottom
        pub fn move_to(x: i32, y: i32) {
            let mouse = Mouse::new();
            mouse.move_to(x, y).expect("Unable to move mouse");
        }

        pub fn move_to_dbclick(x: i32, y: i32) {
            let mouse = Mouse::new();
            mouse.move_to(x, y).expect("Unable to move mouse");
            mouse.press(&Keys::LEFT).expect("Unable to press button");
            mouse.release(&Keys::LEFT).expect("Unable to press button");
            mouse.press(&Keys::LEFT).expect("Unable to press button");
            mouse.release(&Keys::LEFT).expect("Unable to press button");
        }

        /// x is from left to right
        ///
        /// y is from top to bottom
        ///
        /// duration is sleep duration
        pub fn move_to_with_duration(x: i32, y: i32, duration: Duration) {
            let mouse = Mouse::new();
            sleep(duration);
            mouse.move_to(x, y).expect("Unable to move mouse");
        }
        /// x is from left to right
        ///
        /// y is from top to bottom
        pub fn move_to_press_left(x: i32, y: i32) {
            let mouse = Mouse::new();
            move_to(x, y);
            mouse.press(&Keys::LEFT).expect("Unable to press button");
            mouse
                .release(&Keys::LEFT)
                .expect("Unable to release button");
        }
        /// x is from left to right
        ///
        /// y is from top to bottom
        pub fn move_to_press_right(x: i32, y: i32) {
            let mouse = Mouse::new();
            move_to(x, y);
            mouse.press(&Keys::RIGHT).expect("Unable to press button");
            mouse
                .release(&Keys::RIGHT)
                .expect("Unable to release button");
        }
        /// key just can be 'left' or 'right' or 'middle'
        pub fn press(key: impl AsRef<str>) {
            let mouse = Mouse::new();
            match key.as_ref() {
                "left" => {
                    mouse.press(&Keys::LEFT).expect("Unable to press button");
                }
                "right" => {
                    mouse.press(&Keys::RIGHT).expect("Unable to press button");
                }
                "middle" => {
                    mouse.press(&Keys::MIDDLE).expect("Unable to press button");
                }
                _ => {}
            }
        }

        /// key just can be 'left' or 'right' or 'middle'
        pub fn release(key: impl AsRef<str>) {
            let mouse = Mouse::new();
            match key.as_ref() {
                "left" => {
                    mouse
                        .release(&Keys::LEFT)
                        .expect("Unable to release button");
                }
                "right" => {
                    mouse
                        .release(&Keys::RIGHT)
                        .expect("Unable to release button");
                }
                "middle" => {
                    mouse
                        .release(&Keys::MIDDLE)
                        .expect("Unable to press button");
                }
                _ => {}
            }
        }

        ///
        /// ```
        ///    use doe::mouse;
        ///    let script = r#"
        ///move_to::217::135
        ///sleep::1
        ///move_to_press_left::189::137
        ///sleep::1
        ///move_to_press_left::198::185
        ///sleep::1
        ///move_drag::655::343::678::330
        ///sleep::1
        /// # script comment
        ///move_to_paste::479::715::page demo is ok
        ///    "#
        ///    .trim();
        ///    mouse::mouse_automater(script);
        /// ```

        #[cfg(feature = "clip")]
        pub fn mouse_automater(script: impl ToString) -> () {
            use crate::mouse::*;
            use std::thread::sleep;
            use std::time::Duration;
            for script in script.to_string().split("\n") {
                let script = script.trim().to_string();
                if script.starts_with("#") || script.starts_with("//") {
                    continue;
                }
                let tokens: Vec<&str> = script.split("::").map(|s| s.trim()).collect();
                match tokens.as_slice() {
                    ["move_to", x, y] => {
                        let x = x.parse::<i32>().unwrap();
                        let y = y.parse::<i32>().unwrap();
                        move_to(x, y)
                    }
                    ["sleep", t] => {
                        let t = t.parse::<f64>().unwrap();
                        sleep(Duration::from_secs_f64(t))
                    }
                    ["move_to_press_left", x, y] => {
                        let x = x.parse::<i32>().unwrap();
                        let y = y.parse::<i32>().unwrap();
                        move_to_press_left(x, y)
                    }
                    ["move_to_press_right", x, y] => {
                        let x = x.parse::<i32>().unwrap();
                        let y = y.parse::<i32>().unwrap();
                        move_to_press_right(x, y)
                    }
                    ["move_to_paste", x, y, content] => {
                        let x = x.parse::<i32>().unwrap();
                        let y = y.parse::<i32>().unwrap();
                        move_to_paste(x, y, content)
                    }
                    _ => {
                        if let Some(command) = tokens.iter().nth(0) {
                            if command == &"move_drag" {
                                let x1 = tokens.iter().nth(1).unwrap().parse::<i32>().unwrap();
                                let x2 = tokens.iter().nth(2).unwrap().parse::<i32>().unwrap();
                                let y1 = tokens.iter().nth(3).unwrap().parse::<i32>().unwrap();
                                let y2 = tokens.iter().nth(4).unwrap().parse::<i32>().unwrap();
                                mouse_drag((x1, y1).into(), (x2, y2).into())
                            }
                        } else {
                            println!("Unsupported input!");
                        }
                    }
                }
            }
        }
    }
    #[cfg(target_os = "linux")]
    pub mod linux {
        use super::super::Point;
        use mouse_rs::{types::keys::Keys, Mouse};
        use std::thread::sleep;
        use std::time::Duration;
        /// listen for mouse movement and print position
        ///
        /// ```rust
        /// use doe::mouse::listen_mouse_pisition;
        /// listen_mouse_pisition();
        /// ```
        ///
        pub fn listen_mouse_position() {
            let mut prev_position = Mouse::new().get_position().unwrap();
                let mouse = Mouse::new();
            loop {
                let position = mouse.get_position().unwrap();
                if position.to_string() != prev_position.to_string() {
                    println!("move_to({},{});", position.x, position.y);
                    prev_position = position;
                }
                sleep(Duration::from_secs_f64(0.05));
            }
        }
        pub fn listen_mouse_position_by_xdotool() {
            use std::{thread, time::Duration};
            loop {
                use xdotool::mouse::get_mouse_location;
                let point = String::from_utf8_lossy(&get_mouse_location().stdout)
                    .to_string()
                    .trim()
                    .to_string();
                println!("{}", point);
                thread::sleep(Duration::from_millis(100));
            }
        }
        pub fn mouse_drag(from: Point, to: Point) {}
        ///move_to_paste
        ///```rust
        ///use doe::mouse::*;
        ///move_to_paste(293, 404, "hello world");
        /// ```
        #[cfg(feature = "clip")]
        pub fn move_to_paste(x: i32, y: i32, content: impl ToString) {
            use crate::clipboard::set_clipboard;
            use crate::keyboard::key_press;
            use crate::keyboard::key_release;
            use crate::keyboard::KeyCode;
            use crate::mouse::move_to_press_left;
            set_clipboard(content.to_string()).expect("set clipboad error");
            move_to_press_left(x, y);
            key_press("ctrl+v");
            key_release("ctrl+v");
        }
    }

    #[cfg(target_os = "macos")]
    pub mod macos {
        use crate::mouse::move_to_press_left;

        use super::super::Point;
        use mouse_rs::{types::keys::Keys, Mouse};
        use std::thread::sleep;
        use std::time::Duration;
        pub fn listen_mouse_position() {
            let mut prev_position = get_mouse_position_by_core_graphics();
            loop {
                let position = get_mouse_position_by_core_graphics();
                if position != prev_position {
                    println!("move_to({},{});", position.0, position.1);
                    prev_position = position;
                }
                sleep(Duration::from_secs_f64(0.1));
            }
        }

        pub fn get_mouse_position_by_core_graphics() -> (f64, f64) {
            use core_graphics::event::CGEvent;
            use core_graphics::event::CGKeyCode;
            use core_graphics::event_source::CGEventSource;
            use core_graphics::{event::KeyCode, event_source::CGEventSourceStateID};
            let state_id: CGEventSourceStateID = CGEventSourceStateID::HIDSystemState;
            let source: CGEventSource = CGEventSource::new(state_id).unwrap();
            let keycode: CGKeyCode = KeyCode::SPACE;
            let event = CGEvent::new_keyboard_event(source, keycode, true).unwrap();
            let xy = event.location();
            (xy.x, xy.y)
        }
        ///move_to_paste
        ///```rust
        ///use doe::mouse::*;
        ///move_to_paste(293, 404, "hello world");
        /// ```
        #[cfg(feature = "clip")]
        pub fn move_to_paste(x: i32, y: i32, content: impl ToString) {
            use crate::clipboard::set_clipboard;
            use crate::keyboard::key_press;
            use crate::keyboard::key_release;
            use crate::keyboard::KeyCode;
            set_clipboard(content.to_string()).expect("set clipboad error");
            move_to_press_left(x, y);
            key_press(KeyCode::CONTROL);
            key_press(KeyCode::V);
            key_release(KeyCode::CONTROL);
            key_release(KeyCode::V);
        }
        /// mouse_drag
        /// ```rust
        ///fn main(){
        ///    use doe::mouse::mouse_drag;
        ///    mouse_drag((543,446).into(), (590,460).into(),"left");
        ///}
        /// ```
        pub fn mouse_left_drag(from: Point, to: Point) {
            use core_graphics::event::{CGEvent, CGEventTapLocation, CGEventType, CGMouseButton};
            use core_graphics::event_source::{CGEventSource, CGEventSourceStateID};
            use core_graphics::geometry::CGPoint;
            let state_id: CGEventSourceStateID = CGEventSourceStateID::HIDSystemState;
            let event_source: CGEventSource = CGEventSource::new(state_id).unwrap();

            // 创建鼠标按下事件
            let mouse_down_event = CGEvent::new_mouse_event(
                event_source.clone(),
                CGEventType::LeftMouseDown,
                CGPoint::new(from.x as f64, from.y as f64),
                CGMouseButton::Left,
            )
            .unwrap();
            mouse_down_event.post(CGEventTapLocation::HID);

            // 创建鼠标移动事件
            let mouse_move_event = CGEvent::new_mouse_event(
                event_source.clone(),
                CGEventType::MouseMoved,
                CGPoint::new(to.x as f64, to.y as f64),
                CGMouseButton::Left,
            )
            .unwrap();
            mouse_move_event.post(CGEventTapLocation::HID);

            // 创建鼠标释放事件
            let mouse_up_event = CGEvent::new_mouse_event(
                event_source.clone(),
                CGEventType::LeftMouseUp,
                CGPoint::new(to.x as f64, to.y as f64),
                CGMouseButton::Left,
            )
            .unwrap();
            mouse_up_event.post(CGEventTapLocation::HID);
        }
        #[cfg(target_os = "macos")]
        pub fn mouse_drag(from: Point, to: Point) {
            use core_graphics::event::{CGEvent, CGEventTapLocation, CGEventType, CGMouseButton};
            use core_graphics::event_source::{CGEventSource, CGEventSourceStateID};
            use core_graphics::geometry::CGPoint;

            let state_id: CGEventSourceStateID = CGEventSourceStateID::HIDSystemState;
            let event_source: CGEventSource = CGEventSource::new(state_id).unwrap();

            // 创建鼠标按下事件
            let mouse_down_event = CGEvent::new_mouse_event(
                event_source.clone(),
                CGEventType::LeftMouseDown,
                CGPoint::new(from.x as f64, from.y as f64),
                CGMouseButton::Left,
            )
            .unwrap();
            mouse_down_event.post(CGEventTapLocation::HID);

            // 创建鼠标移动事件
            let mouse_move_event = CGEvent::new_mouse_event(
                event_source.clone(),
                CGEventType::MouseMoved,
                CGPoint::new(to.x as f64, to.y as f64),
                CGMouseButton::Left,
            )
            .unwrap();
            mouse_move_event.post(CGEventTapLocation::HID);

            // 创建鼠标释放事件
            let mouse_up_event = CGEvent::new_mouse_event(
                event_source,
                CGEventType::LeftMouseUp,
                CGPoint::new(to.x as f64, to.y as f64),
                CGMouseButton::Left,
            )
            .unwrap();
            mouse_up_event.post(CGEventTapLocation::HID);
        }
    }

    #[cfg(target_os = "windows")]
    pub mod windows {
        use crate::mouse::move_to_press_left;
        use crate::{DebugPrint, Sleep};

        use super::super::Point;
        use mouse_rs::{types::keys::Keys, Mouse};
        use std::path::PathBuf;
        use std::thread::sleep;
        use std::time::Duration;

        pub fn mouse_position_color(x: i32, y: i32) -> String {
            use winapi::um::wingdi::GetPixel;
            use winapi::um::winuser::{GetDC, ReleaseDC};
            unsafe {
                // Get the device context of the screen
                let hdc = GetDC(std::ptr::null_mut());
                // Get the pixel color at the mouse position
                let pixel_color = GetPixel(hdc, x, y);
                // Release the device context
                ReleaseDC(std::ptr::null_mut(), hdc);
                // Extract the RGB components from the pixel color
                let red = pixel_color as u8;
                let green = (pixel_color >> 8) as u8;
                let blue = (pixel_color >> 16) as u8;
                // Convert the RGB components to a hexadecimal color value
                let color_hex = format!("{:02X}{:02X}{:02X}", red, green, blue);
                color_hex
            }
        }

        pub fn listen_mouse_position() {
            use winapi::shared::windef::POINT;
            use winapi::um::winuser::GetCursorPos;
            let mut prev_position: POINT = POINT { x: 0, y: 0 };
            unsafe {
                if GetCursorPos(&mut prev_position) == 0 {
                    return;
                }
                loop {
                    let mut position: POINT = POINT { x: 0, y: 0 };
                    if GetCursorPos(&mut position) == 0 {
                        break;
                    }
                    if (position.x != prev_position.x) && (position.y != prev_position.y) {
                        println!("move_to({},{});", position.x, position.y);
                        prev_position = position;
                    }
                }
                sleep(Duration::from_secs_f64(0.1));
            }
        }

        pub fn listen_mouse_position_append_to_file() {
            use winapi::shared::windef::POINT;
            use winapi::um::winuser::GetCursorPos;
            let mut prev_position: POINT = POINT { x: 0, y: 0 };
            unsafe {
                if GetCursorPos(&mut prev_position) == 0 {
                    return;
                }
                loop {
                    let mut position: POINT = POINT { x: 0, y: 0 };
                    if GetCursorPos(&mut position) == 0 {
                        break;
                    }
                    if (position.x != prev_position.x) && (position.y != prev_position.y) {
                        let file_path = "listen_all.bin";
                        if let Ok(_) = crate::append_data_to_file(
                            file_path,
                            format!("move_to({},{});\n", position.x, position.y),
                        ) {}
                        prev_position = position;
                    }
                }
                sleep(Duration::from_secs_f64(0.1));
            }
        }

        ///move_to_paste
        ///```rust
        ///use doe::mouse::*;
        ///move_to_paste(293, 404, "hello world");
        /// ```
        #[cfg(feature = "clip")]
        pub fn move_to_paste(x: i32, y: i32, content: impl ToString) {
            use crate::clipboard::set_clipboard;
            use crate::keyboard::key_click;
            use crate::keyboard::key_press;
            use crate::keyboard::key_release;
            use crate::keyboard::KeyCode;
            set_clipboard(content.to_string()).expect("set clipboad error");
            move_to_press_left(x, y);
            key_click("ctrl+v");
        }
        /// mouse_drag
        /// ```rust
        ///fn main(){
        ///    use doe::mouse::mouse_drag;
        ///    mouse_drag((543,446).into(), (590,460).into());
        ///}
        /// ```
        pub fn mouse_drag(from: Point, to: Point) {
            use std::thread::sleep;
            use std::time::Duration;
            unsafe {
                use winapi::ctypes::c_int;
                use winapi::um::winuser::{
                    GetSystemMetrics, SendInput, INPUT, INPUT_MOUSE, MOUSEEVENTF_ABSOLUTE,
                    MOUSEEVENTF_LEFTDOWN, MOUSEEVENTF_LEFTUP, MOUSEEVENTF_MOVE, MOUSEINPUT,
                    SM_CXSCREEN, SM_CYSCREEN,
                };
                // Screen size
                let screen_x: c_int = GetSystemMetrics(SM_CXSCREEN);
                let screen_y: c_int = GetSystemMetrics(SM_CYSCREEN);

                let mut input = INPUT::default();
                input.type_ = INPUT_MOUSE;

                let mi: MOUSEINPUT = MOUSEINPUT {
                    dx: (from.x * 65535) / screen_x, // Normalized absolute coordinate for x (horizontal)
                    dy: (from.y * 65535) / screen_y, // Normalized absolute coordinate for y (vertical)
                    mouseData: 0,
                    dwFlags: MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE | MOUSEEVENTF_LEFTDOWN,
                    time: 0,
                    dwExtraInfo: 0,
                };

                input.u.mi_mut().clone_from(&mi);
                SendInput(1, &mut input, std::mem::size_of::<INPUT>() as i32); // Mouse down at 'from' point
                sleep(Duration::from_secs_f64(0.1));
                // We need to update the dx, dy for 'to' point to complete the drag
                input.u.mi_mut().dx = (to.x * 65535) / screen_x; // Normalized absolute coordinate for x (horizontal)
                input.u.mi_mut().dy = (to.y * 65535) / screen_y; // Normalized absolute coordinate for y (vertical)
                input.u.mi_mut().dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE;
                SendInput(1, &mut input, std::mem::size_of::<INPUT>() as i32); // Mouse move to 'to' point
                                                                               // Update the dwFlags for a mouse up event
                input.u.mi_mut().dwFlags =
                    MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE | MOUSEEVENTF_LEFTUP;
                SendInput(1, &mut input, std::mem::size_of::<INPUT>() as i32);
                // Mouse up at 'to' point
            }
        }

        pub fn listen_mouse_click() {
            use winapi::shared::minwindef::{LPARAM, WPARAM};
            use winapi::um::{libloaderapi, winuser};

            unsafe extern "system" fn mouse_hook_callback(
                code: i32,
                wparam: WPARAM,
                lparam: LPARAM,
            ) -> LPARAM {
                if code >= 0 {
                    if wparam == winuser::WM_LBUTTONDOWN as usize {
                        println!("press(\"left\");");
                    } else if wparam == winuser::WM_LBUTTONUP as usize {
                        println!("release(\"left\");");
                    } else if wparam == winuser::WM_RBUTTONDOWN as usize {
                        println!("press(\"right\");");
                    } else if wparam == winuser::WM_RBUTTONUP as usize {
                        println!("release(\"right\");");
                    } else if wparam == winuser::WM_MBUTTONDOWN as usize {
                        println!("press(\"middle\");");
                    } else if wparam == winuser::WM_MBUTTONUP as usize {
                        println!("release(\"middle\");");
                    }
                }
                winuser::CallNextHookEx(0 as winapi::shared::windef::HHOOK, code, wparam, lparam)
            }

            let h_instance = unsafe { libloaderapi::GetModuleHandleW(std::ptr::null_mut()) };
            let hook = unsafe {
                winuser::SetWindowsHookExW(
                    winuser::WH_MOUSE_LL,
                    Some(mouse_hook_callback),
                    h_instance,
                    0,
                )
            };
            if hook.is_null() {
                panic!("Failed to install hook");
            }

            let mut msg = winapi::um::winuser::MSG::default();
            while unsafe { winuser::GetMessageW(&mut msg, std::ptr::null_mut(), 0, 0) } > 0 {
                unsafe {
                    winuser::TranslateMessage(&msg);
                    winuser::DispatchMessageW(&msg);
                }
            }

            unsafe { winuser::UnhookWindowsHookEx(hook) };
        }

        pub fn listen_mouse_click_append_to_file() {
            use winapi::shared::minwindef::{LPARAM, WPARAM};
            use winapi::um::{libloaderapi, winuser};

            unsafe extern "system" fn mouse_hook_callback(
                code: i32,
                wparam: WPARAM,
                lparam: LPARAM,
            ) -> LPARAM {
                if code >= 0 {
                    if wparam == winuser::WM_LBUTTONDOWN as usize {
                        let file_path = "listen_all.bin";
                        if let Ok(_) =
                            crate::append_data_to_file(file_path, format!("press(\"left\");\n"))
                        {
                        }
                    } else if wparam == winuser::WM_LBUTTONUP as usize {
                        let file_path = "listen_all.bin";
                        if let Ok(_) =
                            crate::append_data_to_file(file_path, format!("release(\"left\");\n"))
                        {
                        }
                    } else if wparam == winuser::WM_RBUTTONDOWN as usize {
                        let file_path = "listen_all.bin";
                        if let Ok(_) =
                            crate::append_data_to_file(file_path, format!("press(\"right\");\n"))
                        {
                        }
                    } else if wparam == winuser::WM_RBUTTONUP as usize {
                        let file_path = "listen_all.bin";
                        if let Ok(_) =
                            crate::append_data_to_file(file_path, format!("release(\"right\");\n"))
                        {
                        }
                    } else if wparam == winuser::WM_MBUTTONDOWN as usize {
                        let file_path = "listen_all.bin";
                        if let Ok(_) =
                            crate::append_data_to_file(file_path, format!("press(\"middle\");\n"))
                        {
                        }
                    } else if wparam == winuser::WM_MBUTTONUP as usize {
                        let file_path = "listen_all.bin";
                        if let Ok(_) =
                            crate::append_data_to_file(file_path, format!("release(\"middle\");\n"))
                        {
                        }
                    }
                }
                winuser::CallNextHookEx(0 as winapi::shared::windef::HHOOK, code, wparam, lparam)
            }

            let h_instance = unsafe { libloaderapi::GetModuleHandleW(std::ptr::null_mut()) };
            let hook = unsafe {
                winuser::SetWindowsHookExW(
                    winuser::WH_MOUSE_LL,
                    Some(mouse_hook_callback),
                    h_instance,
                    0,
                )
            };
            if hook.is_null() {
                panic!("Failed to install hook");
            }

            let mut msg = winapi::um::winuser::MSG::default();
            while unsafe { winuser::GetMessageW(&mut msg, std::ptr::null_mut(), 0, 0) } > 0 {
                unsafe {
                    winuser::TranslateMessage(&msg);
                    winuser::DispatchMessageW(&msg);
                }
            }

            unsafe { winuser::UnhookWindowsHookEx(hook) };
        }
        pub fn listen_mouse_scroll() {
            use winapi::shared::minwindef::{LPARAM, WPARAM};
            use winapi::um::{libloaderapi, winuser};

            unsafe extern "system" fn scroll_hook_callback(
                code: i32,
                wparam: WPARAM,
                lparam: LPARAM,
            ) -> LPARAM {
                if code >= 0 {
                    let msllhook = *(lparam as *const winuser::MSLLHOOKSTRUCT);
                    if msllhook.flags & winuser::LLMHF_INJECTED == 0 {
                        // Check if it's a scroll event
                        if wparam == winuser::WM_MOUSEWHEEL as usize
                            || wparam == winuser::WM_MOUSEHWHEEL as usize
                        {
                            let flag = msllhook;
                            println!("move_to({},{});", flag.pt.x, flag.pt.y);
                            if flag.mouseData == 4287102976 {
                                println!("scoll_down();");
                            } else if flag.mouseData == 7864320 {
                                println!("scoll_up();");
                            }
                        }
                    }
                }
                winuser::CallNextHookEx(0 as winapi::shared::windef::HHOOK, code, wparam, lparam)
            }

            let h_instance = unsafe { libloaderapi::GetModuleHandleW(std::ptr::null_mut()) };
            let hook = unsafe {
                winuser::SetWindowsHookExW(
                    winuser::WH_MOUSE_LL,
                    Some(scroll_hook_callback),
                    h_instance,
                    0,
                )
            };
            if hook.is_null() {
                panic!("Failed to install hook");
            }

            let mut msg = winapi::um::winuser::MSG::default();
            while unsafe { winuser::GetMessageW(&mut msg, std::ptr::null_mut(), 0, 0) } > 0 {
                unsafe {
                    winuser::TranslateMessage(&msg);
                    winuser::DispatchMessageW(&msg);
                }
            }

            unsafe { winuser::UnhookWindowsHookEx(hook) };
        }

        pub fn listen_mouse_scroll_append_to_file() {
            use winapi::shared::minwindef::{LPARAM, WPARAM};
            use winapi::um::{libloaderapi, winuser};

            unsafe extern "system" fn scroll_hook_callback(
                code: i32,
                wparam: WPARAM,
                lparam: LPARAM,
            ) -> LPARAM {
                if code >= 0 {
                    let msllhook = *(lparam as *const winuser::MSLLHOOKSTRUCT);
                    if msllhook.flags & winuser::LLMHF_INJECTED == 0 {
                        // Check if it's a scroll event
                        if wparam == winuser::WM_MOUSEWHEEL as usize
                            || wparam == winuser::WM_MOUSEHWHEEL as usize
                        {
                            let flag = msllhook;

                            // println!("move_to({},{});", flag.pt.x, flag.pt.y);
                            if flag.mouseData == 4287102976 {
                                let file_path = "listen_all.bin";
                                if let Ok(_) = crate::append_data_to_file(
                                    file_path,
                                    format!("scoll_down();\n"),
                                ) {}
                            } else if flag.mouseData == 7864320 {
                                let file_path = "listen_all.bin";
                                if let Ok(_) =
                                    crate::append_data_to_file(file_path, format!("scoll_up();\n"))
                                {
                                }
                            }
                        }
                    }
                }
                winuser::CallNextHookEx(0 as winapi::shared::windef::HHOOK, code, wparam, lparam)
            }

            let h_instance = unsafe { libloaderapi::GetModuleHandleW(std::ptr::null_mut()) };
            let hook = unsafe {
                winuser::SetWindowsHookExW(
                    winuser::WH_MOUSE_LL,
                    Some(scroll_hook_callback),
                    h_instance,
                    0,
                )
            };
            if hook.is_null() {
                panic!("Failed to install hook");
            }

            let mut msg = winapi::um::winuser::MSG::default();
            while unsafe { winuser::GetMessageW(&mut msg, std::ptr::null_mut(), 0, 0) } > 0 {
                unsafe {
                    winuser::TranslateMessage(&msg);
                    winuser::DispatchMessageW(&msg);
                }
            }

            unsafe { winuser::UnhookWindowsHookEx(hook) };
        }

        /// listen_all keyboard mouse events
        /// ```rust
        /// use doe::mouse::listen_all;
        ///  // listen_all();
        /// ```
        ///
        /// `cargo r > script`
        ///
        pub fn listen_all() {
            use crate::keyboard::listen_keybord;
            use crate::mouse::listen_mouse_click;
            use crate::mouse::listen_mouse_position;
            use crate::mouse::listen_mouse_scroll;
            let t1 = std::thread::spawn(|| {
                listen_keybord();
            });
            let t2 = std::thread::spawn(|| {
                listen_mouse_position();
            });
            let t3 = std::thread::spawn(|| {
                listen_mouse_scroll();
            });
            let t4 = std::thread::spawn(|| {
                listen_mouse_click();
            });
            t1.join().unwrap();
            t2.join().unwrap();
            t3.join().unwrap();
            t4.join().unwrap();
        }

        pub async fn listen_all_append_to_file() -> Result<PathBuf, String> {
            use crate::keyboard::listen_keybord_append_to_file;
            use crate::mouse::listen_mouse_click_append_to_file;
            use crate::mouse::listen_mouse_position_append_to_file;
            use crate::mouse::listen_mouse_scroll_append_to_file;

            let t1 = std::thread::spawn(|| {
                listen_mouse_position_append_to_file();
            });

            let t2 = std::thread::spawn(|| {
                listen_keybord_append_to_file();
            });

            let t3 = std::thread::spawn(|| {
                listen_mouse_scroll_append_to_file();
            });

            let t4 = std::thread::spawn(|| {
                listen_mouse_click_append_to_file();
            });

            t1.join();
            t2.join();
            t3.join();
            t4.join();

            let file_path = "listen_all.bin".to_path_buf();
            return Ok(file_path);
        }

        use crate::traits::traits::Str;
        pub fn for_loop_script(script: impl ToString) -> Result<String, String> {
            let mut new_script: Vec<String> = vec![];
            let mut for_start_index_list = vec![];
            let mut for_end_index_list = vec![];
            let script_vec = script.to_string().trim_lines().split_to_vec("\n");
            script_vec.iter().enumerate().for_each(|(index, line)| {
                if line.starts_with("for") {
                    if let Some(for_num) = line.split_to_vec(" ").last() {
                        for_start_index_list.push((index + 1, for_num.parse::<usize>().unwrap()));
                    }
                } else if line.starts_with("next") {
                    for_end_index_list.push(index);
                }
            });
            if for_start_index_list.len() != for_end_index_list.len() {
                return Err("for_start_index_list len not equal to for_end_index_list".to_string());
            } else {
                let mut old_code = script_vec.join("\n");
                for for_index in 0..for_end_index_list.len() {
                    let for_range = (
                        for_start_index_list[for_index].0,
                        for_end_index_list[for_index],
                    );
                    if let Some(in_for_code) = &script_vec.get(for_range.0..for_range.1) {
                        let in_for_code = in_for_code.join("\n");
                        let repeat_in_for_code = in_for_code
                            .push_back("\n")
                            .repeat(for_start_index_list[for_index].1);
                        old_code = old_code.replace(&in_for_code, &repeat_in_for_code);
                    }
                }
                let old_code_vec = old_code.split_to_vec("\n");
                let mut res_code_vec = vec![];
                for line in old_code_vec {
                    if line.starts_with("for") || line.starts_with("next") {
                    } else {
                        res_code_vec.push(line);
                    }
                }
                return Ok(res_code_vec.join("\n").replace("\n\n", "\n"));
            }
        }

        pub fn smiplify_script(script: impl ToString) -> String {
            let mut new_script = vec![];
            let mut in_move_to_block = false;
            let mut first_move_to = String::new();
            let mut last_move_to = String::new();
            let script_vec = script.to_string().trim_lines().split_to_vec("\n");
            script_vec.iter().enumerate().for_each(|(index, line)| {
                if line.starts_with("move_to") {
                    if !in_move_to_block {
                        in_move_to_block = true;
                        first_move_to = line.to_string();
                        last_move_to = line.to_string(); // Reset last_move_to for a new block
                    } else {
                        // Update last_move_to for the current block
                        last_move_to = line.to_string();
                    }
                } else {
                    if in_move_to_block {
                        new_script.push(first_move_to.to_string());
                        if first_move_to != last_move_to {
                            new_script.push(last_move_to.to_string());
                        }
                        in_move_to_block = false;
                    }
                    new_script.push(line.to_string());
                }
            });
            new_script.join("\n")
        }

        /// automater
        ///
        /// generate script by
        /// ```rust
        /// fn gen(){
        ///     use doe::mouse::listen_all;
        ///     listen_all();
        /// }
        /// ```
        /// script
        ///```sh
        /// scoll_up();
        /// move_to(342,515);
        /// scoll_down();
        /// move_to(304,454);
        /// key_release(8);
        /// press("left");
        ///
        /// //also supprts for loop
        /// for 10
        /// key_press(162);
        /// move_to_paste(150,200,"clipbload");
        /// move_to_dbclick(100,200);
        /// next
        /// ```
        /// run script
        /// ```rust
        ///  use doe::mouse::automater;
        ///  let script =  include_str!("../script");
        ///  automater(script);
        /// ```
        ///
        #[cfg(feature = "clip")]
        pub fn automater(script: impl ToString) -> () {
            use crate::keyboard::*;
            use crate::mouse::*;
            use crate::DebugPrint;
            use std::thread::sleep;
            use std::time::Duration;
            let smiplify_script = smiplify_script(script.to_string());
            let for_loop_script = for_loop_script(smiplify_script.to_string()).unwrap();
            let t1 = std::thread::spawn(move || {
                for script in for_loop_script.split("\n") {
                    let script = script.trim().to_string();
                    if script.starts_with("#") || script.starts_with("//") {
                        continue;
                    }
                    let script = script
                        .split_to_vec("//")
                        .first()
                        .unwrap_or(&"".to_string())
                        .to_string();
                    if script.starts_with("move_to(") {
                        let x = script
                            .clone()
                            .replace("move_to(", "")
                            .replace(");", "")
                            .to_string()
                            .split(",")
                            .nth(0)
                            .unwrap()
                            .parse::<i32>()
                            .unwrap();
                        let y = script
                            .clone()
                            .replace("move_to(", "")
                            .replace(");", "")
                            .to_string()
                            .split(",")
                            .nth(1)
                            .unwrap()
                            .parse::<i32>()
                            .unwrap();
                        move_to(x, y);
                    } else if script.starts_with("move_to_press_left(") {
                        let x = script
                            .clone()
                            .replace("move_to_press_left(", "")
                            .replace(");", "")
                            .to_string()
                            .split(",")
                            .nth(0)
                            .unwrap()
                            .parse::<i32>()
                            .unwrap();
                        let y = script
                            .clone()
                            .replace("move_to_press_left(", "")
                            .replace(");", "")
                            .to_string()
                            .split(",")
                            .nth(1)
                            .unwrap()
                            .to_string()
                            .trim()
                            .parse::<i32>()
                            .unwrap();

                        move_to_press_left(x, y);
                    } else if script.starts_with("move_to_press_right(") {
                        let x = script
                            .clone()
                            .replace("move_to_press_right(", "")
                            .replace(");", "")
                            .to_string()
                            .split(",")
                            .nth(0)
                            .unwrap()
                            .parse::<i32>()
                            .unwrap();
                        let y = script
                            .clone()
                            .replace("move_to_press_right(", "")
                            .replace(");", "")
                            .to_string()
                            .split(",")
                            .nth(1)
                            .unwrap()
                            .parse::<i32>()
                            .unwrap();
                        move_to_press_right(x, y);
                    } else if script.starts_with("move_to_dbclick(") {
                        let x = script
                            .clone()
                            .replace("move_to_dbclick(", "")
                            .replace(");", "")
                            .to_string()
                            .split(",")
                            .nth(0)
                            .unwrap()
                            .parse::<i32>()
                            .unwrap();
                        let y = script
                            .clone()
                            .replace("move_to_dbclick(", "")
                            .replace(");", "")
                            .to_string()
                            .split(",")
                            .nth(1)
                            .unwrap()
                            .parse::<i32>()
                            .unwrap();
                        mouse::move_to_dbclick(x, y);
                    } else if script.starts_with("move_to_paste(") {
                        let x = script
                            .clone()
                            .replace("move_to_paste(", "")
                            .replace(");", "")
                            .to_string()
                            .split(",")
                            .nth(0)
                            .unwrap()
                            .trim()
                            .parse::<i32>()
                            .unwrap();
                        let y = script
                            .clone()
                            .replace("move_to_paste(", "")
                            .replace(");", "")
                            .to_string()
                            .trim()
                            .split(",")
                            .nth(1)
                            .unwrap()
                            .to_string()
                            .trim()
                            .parse::<i32>()
                            .unwrap();
                        let content = script
                            .clone()
                            .replace("move_to_paste(", "")
                            .replace(");", "")
                            .to_string()
                            .split(",")
                            .nth(2)
                            .unwrap()
                            .to_string()
                            .replace("\"", "")
                            .trim()
                            .to_string();
                        content.dprintln();
                        move_to_paste(x, y, content);
                    } else if script.starts_with("scoll_down(") {
                        scrool(-2);
                    } else if script.starts_with("scoll_up(") {
                        scrool(2);
                    } else if script.starts_with("key_press(") {
                        let keycode = script
                            .clone()
                            .replace("key_press(", "")
                            .replace(");", "")
                            .to_string()
                            .split(",")
                            .nth(0)
                            .unwrap()
                            .parse::<u16>()
                            .unwrap();
                        key_press(keycode).sleep(0.2);
                    } else if script.starts_with("key_release(") {
                        let keycode = script
                            .clone()
                            .replace("key_release(", "")
                            .replace(");", "")
                            .to_string()
                            .split(",")
                            .nth(0)
                            .unwrap()
                            .parse::<u16>()
                            .unwrap();
                        key_release(keycode).sleep(0.5);
                    } else if script.starts_with("press(") {
                        let key = script
                            .clone()
                            .replace("press(\"", "")
                            .replace("\");", "")
                            .to_string()
                            .split(",")
                            .nth(0)
                            .unwrap()
                            .to_string();
                        press(key.clone()).sleep(0.2);
                    } else if script.starts_with("key_click(") {
                        let key = script
                            .clone()
                            .replace("key_click(\"", "")
                            .replace("\");", "")
                            .to_string();
                        key_click(&key);
                    } else if script.starts_with("sleep(") {
                        let secs = script
                            .clone()
                            .replace("sleep(", "")
                            .replace(");", "")
                            .to_string()
                            .trim()
                            .parse::<f64>()
                            .unwrap();
                        ().sleep(secs);
                    }
                }
            });

            let t2 = std::thread::spawn(|| {
                exit_if_click_esc();
            });
            t1.join();
            t2.join();
        }
    }

    pub use common::*;

    #[cfg(target_os = "linux")]
    pub use linux::*;

    #[cfg(target_os = "macos")]
    pub use macos::*;

    #[cfg(target_os = "windows")]
    pub use windows::*;
}
#[cfg(feature = "mouse")]
pub use mouse::*;
