use std::{any::Any, rc::Rc};
use dioxus_core::ElementId;
use dioxus_native_core::NodeId;
use freya_elements::events::{
    pointer::PointerType, KeyboardData, MouseData, PointerData, TouchData, WheelData,
};
use torin::prelude::*;
use crate::events::FreyaEvent;
#[derive(Debug, Clone, PartialEq)]
pub struct DomEvent {
    pub name: String,
    pub node_id: NodeId,
    pub element_id: ElementId,
    pub data: DomEventData,
}
impl Eq for DomEvent {}
impl PartialOrd for DomEvent {
    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
        Some(self.cmp(other))
    }
}
impl Ord for DomEvent {
    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
        match self.name.as_str() {
            "mouseleave" | "pointerleave" => {
                if self.name == other.name {
                    std::cmp::Ordering::Equal
                } else {
                    std::cmp::Ordering::Less
                }
            }
            _ => std::cmp::Ordering::Greater,
        }
    }
}
impl DomEvent {
    pub fn does_move_cursor(&self) -> bool {
        return does_event_move_cursor(self.name.as_str());
    }
    pub fn new(
        node_id: NodeId,
        element_id: ElementId,
        event: &FreyaEvent,
        node_area: Option<Area>,
        scale_factor: f64,
    ) -> Self {
        let is_pointer_event = event.is_pointer_event();
        let event_name = event.get_name().to_string();
        match event {
            FreyaEvent::Mouse { cursor, button, .. } => {
                let screen_coordinates = *cursor / scale_factor;
                let element_x =
                    (cursor.x - node_area.unwrap_or_default().min_x() as f64) / scale_factor;
                let element_y =
                    (cursor.y - node_area.unwrap_or_default().min_y() as f64) / scale_factor;
                let event_data = if is_pointer_event {
                    DomEventData::Pointer(PointerData::new(
                        screen_coordinates,
                        (element_x, element_y).into(),
                        PointerType::Mouse {
                            trigger_button: *button,
                        },
                    ))
                } else {
                    DomEventData::Mouse(MouseData::new(
                        screen_coordinates,
                        (element_x, element_y).into(),
                        *button,
                    ))
                };
                Self {
                    node_id,
                    element_id,
                    name: event_name,
                    data: event_data,
                }
            }
            FreyaEvent::Wheel { scroll, .. } => Self {
                node_id,
                element_id,
                name: event_name,
                data: DomEventData::Wheel(WheelData::new(scroll.x, scroll.y)),
            },
            FreyaEvent::Keyboard {
                ref key,
                code,
                modifiers,
                ..
            } => Self {
                node_id,
                element_id,
                name: event_name,
                data: DomEventData::Keyboard(KeyboardData::new(key.clone(), *code, *modifiers)),
            },
            FreyaEvent::Touch {
                location,
                finger_id,
                phase,
                force,
                ..
            } => {
                let element_x = location.x - node_area.unwrap_or_default().min_x() as f64;
                let element_y = location.y - node_area.unwrap_or_default().min_y() as f64;
                let event_data = if is_pointer_event {
                    DomEventData::Pointer(PointerData::new(
                        *location,
                        (element_x, element_y).into(),
                        PointerType::Touch {
                            finger_id: *finger_id,
                            phase: *phase,
                            force: *force,
                        },
                    ))
                } else {
                    DomEventData::Touch(TouchData::new(
                        *location,
                        (element_x, element_y).into(),
                        *finger_id,
                        *phase,
                        *force,
                    ))
                };
                Self {
                    node_id,
                    element_id,
                    name: event_name,
                    data: event_data,
                }
            }
        }
    }
}
#[derive(Debug, Clone, PartialEq)]
pub enum DomEventData {
    Mouse(MouseData),
    Keyboard(KeyboardData),
    Wheel(WheelData),
    Touch(TouchData),
    Pointer(PointerData),
}
impl DomEventData {
    pub fn any(self) -> Rc<dyn Any> {
        match self {
            DomEventData::Mouse(m) => Rc::new(m),
            DomEventData::Keyboard(k) => Rc::new(k),
            DomEventData::Wheel(w) => Rc::new(w),
            DomEventData::Touch(t) => Rc::new(t),
            DomEventData::Pointer(p) => Rc::new(p),
        }
    }
}
pub fn does_event_move_cursor(event_name: &str) -> bool {
    ["pointerover", "pointerenter", "mouseover", "mouseenter"].contains(&event_name)
}