use serde::{Serialize, Deserialize};
use std::fmt;
use super::snap_point::SnapCalculator;

#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub enum OsnapMode {
    Endpoint,
    Midpoint,
    Center,
    Node,
    Quadrant,
    Intersection,
    Perpendicular,
    Tangent,
    Parallel,
    Extension,
    Insertion,
    Nearest,
    ApparentIntersection,
    None,
}

impl Default for OsnapMode {
    fn default() -> Self {
        OsnapMode::None
    }
}

impl OsnapMode {
    pub fn to_snap_type(&self) -> super::snap_point::SnapType {
        match self {
            OsnapMode::Endpoint => super::snap_point::SnapType::EndPoint,
            OsnapMode::Midpoint => super::snap_point::SnapType::MidPoint,
            OsnapMode::Center => super::snap_point::SnapType::Center,
            OsnapMode::Node => super::snap_point::SnapType::Node,
            OsnapMode::Quadrant => super::snap_point::SnapType::Quadrant,
            OsnapMode::Intersection => super::snap_point::SnapType::Intersection,
            OsnapMode::Perpendicular => super::snap_point::SnapType::Perpendicular,
            OsnapMode::Tangent => super::snap_point::SnapType::Tangent,
            OsnapMode::Parallel => super::snap_point::SnapType::Parallel,
            OsnapMode::Extension => super::snap_point::SnapType::Extension,
            OsnapMode::Nearest => super::snap_point::SnapType::Nearest,
            OsnapMode::ApparentIntersection => super::snap_point::SnapType::Intersection,
            OsnapMode::Insertion => super::snap_point::SnapType::Nearest,
            OsnapMode::None => super::snap_point::SnapType::None,
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OsnapSettings {
    pub display_osnap_marker: bool,
    pub display_osnap_tooltip: bool,
    pub display_osnap_cursor_tip: bool,
    pub marker_color: (u8, u8, u8),
    pub marker_size: f64,
    pub tooltip_color: (u8, u8, u8),
    pub aperture_box_size: f64,
    pub is_running: bool,
}

impl Default for OsnapSettings {
    fn default() -> Self {
        Self {
            display_osnap_marker: true,
            display_osnap_tooltip: true,
            display_osnap_cursor_tip: true,
            marker_color: (0, 255, 0),
            marker_size: 5.0,
            tooltip_color: (255, 255, 0),
            aperture_box_size: 30.0,
            is_running: false,
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OsnapMarker {
    pub position: crate::geometry::Point,
    pub mode: OsnapMode,
    pub entity_id: super::super::data_structure::ObjectId,
    pub display_text: String,
}

impl Default for OsnapMarker {
    fn default() -> Self {
        Self {
            position: crate::geometry::Point::new(0.0, 0.0, 0.0),
            mode: OsnapMode::None,
            entity_id: super::super::data_structure::ObjectId::null(),
            display_text: String::new(),
        }
    }
}

impl OsnapMarker {
    pub fn new(position: crate::geometry::Point, mode: OsnapMode, entity_id: super::super::data_structure::ObjectId) -> Self {
        let display_text = format!("{:?}", mode);
        Self {
            position,
            mode,
            entity_id,
            display_text,
        }
    }
}

pub struct OsnapTracker {
    settings: OsnapSettings,
    active_modes: Vec<OsnapMode>,
    current_marker: Option<OsnapMarker>,
    tooltip_text: String,
}

impl Default for OsnapTracker {
    fn default() -> Self {
        Self {
            settings: OsnapSettings::default(),
            active_modes: vec![
                OsnapMode::Endpoint,
                OsnapMode::Midpoint,
                OsnapMode::Center,
                OsnapMode::Intersection,
            ],
            current_marker: None,
            tooltip_text: String::new(),
        }
    }
}

impl OsnapTracker {
    pub fn new() -> Self {
        Self::default()
    }

    pub fn enable(&mut self, enabled: bool) {
        self.settings.is_running = enabled;
    }

    pub fn is_enabled(&self) -> bool {
        self.settings.is_running
    }

    pub fn enable_mode(&mut self, mode: OsnapMode, enabled: bool) {
        if enabled {
            if !self.active_modes.contains(&mode) {
                self.active_modes.push(mode);
            }
        } else {
            self.active_modes.retain(|&m| m != mode);
        }
    }

    pub fn get_active_modes(&self) -> &[OsnapMode] {
        &self.active_modes
    }

    pub fn set_marker(&mut self, marker: Option<OsnapMarker>) {
        self.current_marker = marker;
    }

    pub fn get_marker(&self) -> Option<&OsnapMarker> {
        self.current_marker.as_ref()
    }

    pub fn get_tooltip_text(&self) -> &str {
        &self.tooltip_text
    }

    pub fn set_tooltip_text(&mut self, text: impl Into<String>) {
        self.tooltip_text = text.into();
    }

    pub fn settings(&self) -> &OsnapSettings {
        &self.settings
    }

    pub fn settings_mut(&mut self) -> &mut OsnapSettings {
        &mut self.settings
    }

    pub fn track(
        &mut self,
        cursor_point: crate::geometry::Point,
        entities: &[super::super::data_structure::Entity],
        snap_manager: &super::snap_point::SnapManager,
    ) -> Option<OsnapMarker> {
        if !self.settings.is_running {
            return None;
        }

        let mut best_marker: Option<OsnapMarker> = None;
        let mut best_distance = self.settings.aperture_box_size;

        let calculator = super::snap_point::SnapCalculatorImpl;

        for entity in entities {
            for mode in &self.active_modes {
                let snapshot = match mode {
                    OsnapMode::Endpoint => calculator.calculate_endpoint(entity),
                    OsnapMode::Midpoint => calculator.calculate_midpoint(entity),
                    OsnapMode::Center => calculator.calculate_center(entity),
                    OsnapMode::Node => calculator.calculate_node(entity),
                    OsnapMode::Quadrant => calculator.calculate_quadrant(entity).first().cloned(),
                    OsnapMode::Perpendicular => calculator.calculate_perpendicular(entity, cursor_point),
                    OsnapMode::Tangent => calculator.calculate_tangent(entity, cursor_point),
                    OsnapMode::Nearest => calculator.calculate_nearest(entity, cursor_point),
                    OsnapMode::Extension => calculator.calculate_extension(entity, cursor_point, 10.0),
                    _ => None,
                };

                if let Some(snap) = snapshot {
                    let distance = snap.point.distance_to(&cursor_point);
                    if distance < best_distance {
                        best_marker = Some(OsnapMarker::new(snap.point, *mode, snap.entity_id));
                        best_distance = distance;
                    }
                }
            }
        }

        if let Some(ref marker) = best_marker {
            self.set_tooltip_text(format!("{}: {:?}", marker.position, marker.mode));
        } else {
            self.set_tooltip_text(String::new());
        }

        self.current_marker = best_marker.clone();
        best_marker
    }

    pub fn clear(&mut self) {
        self.current_marker = None;
        self.tooltip_text.clear();
    }
}

impl fmt::Display for OsnapTracker {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "OsnapTracker(enabled={}, modes={})",
            self.settings.is_running,
            self.active_modes.len()
        )
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_osnap_mode_conversion() {
        assert_eq!(OsnapMode::Endpoint.to_snap_type(), super::super::snap_point::SnapType::EndPoint);
        assert_eq!(OsnapMode::Midpoint.to_snap_type(), super::super::snap_point::SnapType::MidPoint);
        assert_eq!(OsnapMode::Center.to_snap_type(), super::super::snap_point::SnapType::Center);
    }

    #[test]
    fn test_osnap_settings() {
        let settings = OsnapSettings::default();
        assert!(settings.is_running);
        assert_eq!(settings.marker_size, 5.0);
    }

    #[test]
    fn test_osnap_tracker() {
        let mut tracker = OsnapTracker::new();
        assert!(!tracker.is_enabled());

        tracker.enable(true);
        assert!(tracker.is_enabled());

        tracker.enable_mode(OsnapMode::Endpoint, false);
        assert!(!tracker.get_active_modes().contains(&OsnapMode::Endpoint));
    }
}