use crate::data_structure::{Entity, ObjectId, EntityType, EntityGeometry};
use crate::geometry::{Point, Vector2, Line, Circle, Arc, Ellipse, Polyline, BSpline};
use serde::{Serialize, Deserialize};
use std::collections::HashMap;

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Grip {
    pub id: GripId,
    pub position: Point,
    pub grip_type: GripType,
    pub is_dragging: bool,
    pub is_highlighted: bool,
}

#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub struct GripId(pub u64);

#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub enum GripType {
    Endpoint,
    Midpoint,
    Center,
    Quadrant,
    Intersection,
    Vertex,
    ControlPoint,
    Move,
    Stretch,
    Rotate,
    Scale,
    Mirror,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct EntityGrips {
    pub entity_id: ObjectId,
    pub grips: Vec<Grip>,
    pub is_selected: bool,
}

impl EntityGrips {
    pub fn new(entity_id: ObjectId, entity: &Entity) -> Self {
        let grips = Self::extract_grips(entity);
        Self {
            entity_id,
            grips,
            is_selected: false,
        }
    }
    
    fn extract_grips(entity: &Entity) -> Vec<Grip> {
        let mut grips = Vec::new();
        let mut grip_id = 0;
        
        match &entity.geometry {
            EntityGeometry::Line(line) => {
                grips.push(Grip::new(
                    GripId(grip_id),
                    line.start,
                    GripType::Endpoint,
                ));
                grip_id += 1;
                
                grips.push(Grip::new(
                    GripId(grip_id),
                    line.end,
                    GripType::Endpoint,
                ));
                grip_id += 1;
                
                let midpoint = Point::new(
                    (line.start.x + line.end.x) / 2.0,
                    (line.start.y + line.end.y) / 2.0,
                    0.0,
                );
                grips.push(Grip::new(
                    GripId(grip_id),
                    midpoint,
                    GripType::Midpoint,
                ));
            }
            
            EntityGeometry::Circle(circle) => {
                grips.push(Grip::new(
                    GripId(grip_id),
                    circle.center,
                    GripType::Center,
                ));
                grip_id += 1;
                
                for i in 0..4 {
                    let angle = i as f64 * std::f64::consts::PI / 2.0;
                    let quadrant_point = Point::new(
                        circle.center.x + circle.radius * angle.cos(),
                        circle.center.y + circle.radius * angle.sin(),
                        0.0,
                    );
                    grips.push(Grip::new(
                        GripId(grip_id),
                        quadrant_point,
                        GripType::Quadrant,
                    ));
                    grip_id += 1;
                }
            }
            
            EntityGeometry::Arc(arc) => {
                grips.push(Grip::new(
                    GripId(grip_id),
                    arc.center,
                    GripType::Center,
                ));
                grip_id += 1;
                
                let start_point = Point::new(
                    arc.center.x + arc.radius * arc.start_angle.cos(),
                    arc.center.y + arc.radius * arc.start_angle.sin(),
                    0.0,
                );
                grips.push(Grip::new(
                    GripId(grip_id),
                    start_point,
                    GripType::Endpoint,
                ));
                grip_id += 1;
                
                let end_point = Point::new(
                    arc.center.x + arc.radius * arc.end_angle.cos(),
                    arc.center.y + arc.radius * arc.end_angle.sin(),
                    0.0,
                );
                grips.push(Grip::new(
                    GripId(grip_id),
                    end_point,
                    GripType::Endpoint,
                ));
                grip_id += 1;
                
                let midpoint_angle = (arc.start_angle + arc.end_angle) / 2.0;
                let midpoint = Point::new(
                    arc.center.x + arc.radius * midpoint_angle.cos(),
                    arc.center.y + arc.radius * midpoint_angle.sin(),
                    0.0,
                );
                grips.push(Grip::new(
                    GripId(grip_id),
                    midpoint,
                    GripType::Midpoint,
                ));
            }
            
            EntityGeometry::Ellipse(ellipse) => {
                grips.push(Grip::new(
                    GripId(grip_id),
                    ellipse.center,
                    GripType::Center,
                ));
                grip_id += 1;
                
                let major_end = Point::new(
                    ellipse.center.x + ellipse.semi_major * ellipse.rotation.cos(),
                    ellipse.center.y + ellipse.semi_major * ellipse.rotation.sin(),
                    0.0,
                );
                grips.push(Grip::new(
                    GripId(grip_id),
                    major_end,
                    GripType::Vertex,
                ));
                grip_id += 1;
                
                let minor_end = Point::new(
                    ellipse.center.x + ellipse.semi_minor * (ellipse.rotation + std::f64::consts::PI / 2.0).cos(),
                    ellipse.center.y + ellipse.semi_minor * (ellipse.rotation + std::f64::consts::PI / 2.0).sin(),
                    0.0,
                );
                grips.push(Grip::new(
                    GripId(grip_id),
                    minor_end,
                    GripType::Vertex,
                ));
            }
            
            EntityGeometry::Polyline(polyline) => {
                for (i, vertex) in polyline.vertices.iter().enumerate() {
                    grips.push(Grip::new(
                        GripId(grip_id),
                        *vertex,
                        GripType::Vertex,
                    ));
                    grip_id += 1;
                    
                    if i < polyline.vertices.len().saturating_sub(1) {
                        let next = polyline.vertices[i + 1];
                        let midpoint = Point::new(
                            (vertex.x + next.x) / 2.0,
                            (vertex.y + next.y) / 2.0,
                            0.0,
                        );
                        grips.push(Grip::new(
                            GripId(grip_id),
                            midpoint,
                            GripType::Midpoint,
                        ));
                        grip_id += 1;
                    }
                }
            }
            
            EntityGeometry::BSpline(spline) => {
                for (i, point) in spline.control_points.iter().enumerate() {
                    grips.push(Grip::new(
                        GripId(grip_id),
                        *point,
                        GripType::ControlPoint,
                    ));
                    grip_id += 1;
                }
            }
            
            EntityGeometry::Dimension(_) => {
                if let Some(dimension_geometry) = &entity.dimension_geometry {
                    for point in &dimension_geometry.definition_points {
                        grips.push(Grip::new(
                            GripId(grip_id),
                            *point,
                            GripType::Endpoint,
                        ));
                        grip_id += 1;
                    }
                    
                    if let Some(text_location) = dimension_geometry.text_location {
                        grips.push(Grip::new(
                            GripId(grip_id),
                            text_location,
                            GripType::Midpoint,
                        ));
                    }
                }
            }
            
            _ => {}
        }
        
        grips
    }
    
    pub fn get_grip_by_id(&self, id: GripId) -> Option<&Grip> {
        self.grips.iter().find(|g| g.id == id)
    }
    
    pub fn get_grip_by_id_mut(&mut self, id: GripId) -> Option<&mut Grip> {
        self.grips.iter_mut().find(|g| g.id == id)
    }
    
    pub fn highlight_grip(&mut self, id: GripId) {
        if let Some(grip) = self.get_grip_by_id_mut(id) {
            grip.is_highlighted = true;
        }
    }
    
    pub fn unhighlight_all(&mut self) {
        for grip in &mut self.grips {
            grip.is_highlighted = false;
        }
    }
    
    pub fn contains_point(&self, point: Point, tolerance: f64) -> Option<GripId> {
        for grip in &self.grips {
            if grip.position.distance_to(&point) < tolerance {
                return Some(grip.id);
            }
        }
        None
    }
}

impl Grip {
    pub fn new(id: GripId, position: Point, grip_type: GripType) -> Self {
        Self {
            id,
            position,
            grip_type,
            is_dragging: false,
            is_highlighted: false,
        }
    }
    
    pub fn start_drag(&mut self) {
        self.is_dragging = true;
    }
    
    pub fn end_drag(&mut self) {
        self.is_dragging = false;
    }
    
    pub fn move_to(&mut self, new_position: Point) {
        self.position = new_position;
    }
    
    pub fn move_by(&mut self, delta: Vector2) {
        self.position = Point::new(
            self.position.x + delta.x,
            self.position.y + delta.y,
            0.0,
        );
    }
}

pub struct GripEditor {
    entity_grips: HashMap<ObjectId, EntityGrips>,
    dragged_grip: Option<(ObjectId, GripId)>,
    original_positions: HashMap<(ObjectId, GripId), Point>,
}

impl GripEditor {
    pub fn new() -> Self {
        Self {
            entity_grips: HashMap::new(),
            dragged_grip: None,
            original_positions: HashMap::new(),
        }
    }
    
    pub fn set_entity_grips(&mut self, entity_id: ObjectId, entity: &Entity) {
        let entity_grips = EntityGrips::new(entity_id, entity);
        self.entity_grips.insert(entity_id, entity_grips);
    }
    
    pub fn clear_all_grips(&mut self) {
        self.entity_grips.clear();
        self.dragged_grip = None;
        self.original_positions.clear();
    }
    
    pub fn remove_entity_grips(&mut self, entity_id: &ObjectId) {
        self.entity_grips.remove(entity_id);
    }
    
    pub fn get_all_grips(&self) -> Vec<&Grip> {
        self.entity_grips.values()
            .flat_map(|eg| eg.grips.iter())
            .collect()
    }
    
    pub fn find_grip_at_point(&self, point: Point, tolerance: f64) -> Option<(ObjectId, GripId)> {
        for (entity_id, entity_grips) in &self.entity_grips {
            if let Some(grip_id) = entity_grips.contains_point(point, tolerance) {
                return Some((*entity_id, grip_id));
            }
        }
        None
    }
    
    pub fn start_drag(&mut self, entity_id: ObjectId, grip_id: GripId) -> Option<Point> {
        self.dragged_grip = Some((entity_id, grip_id));
        
        if let Some(entity_grips) = self.entity_grips.get_mut(&entity_id) {
            if let Some(grip) = entity_grips.get_grip_by_id_mut(grip_id) {
                grip.start_drag();
                self.original_positions.insert((entity_id, grip_id), grip.position);
                return Some(grip.position);
            }
        }
        None
    }
    
    pub fn drag_to(&mut self, new_position: Point) -> Vec<(ObjectId, GripId, Point)> {
        let mut moved_grips = Vec::new();
        
        if let Some((entity_id, grip_id)) = self.dragged_grip {
            if let Some(entity_grips) = self.entity_grips.get_mut(&entity_id) {
                if let Some(grip) = entity_grips.get_grip_by_id_mut(grip_id) {
                    let old_position = grip.position;
                    grip.move_to(new_position);
                    moved_grips.push((entity_id, grip_id, old_position));
                }
            }
        }
        
        moved_grips
    }
    
    pub fn end_drag(&mut self) -> Vec<((ObjectId, GripId), Point)> {
        let mut changes = Vec::new();
        
        if let Some((entity_id, grip_id)) = self.dragged_grip {
            if let Some(entity_grips) = self.entity_grips.get_mut(&entity_id) {
                if let Some(grip) = entity_grips.get_grip_by_id_mut(grip_id) {
                    grip.end_drag();
                    if let Some(original) = self.original_positions.remove(&(entity_id, grip_id)) {
                        changes.push(((entity_id, grip_id), original));
                    }
                }
            }
        }
        
        self.dragged_grip = None;
        changes
    }
    
    pub fn cancel_drag(&mut self) {
        if let Some((entity_id, grip_id)) = self.dragged_grip {
            if let Some(entity_grips) = self.entity_grips.get_mut(&entity_id) {
                if let Some(grip) = entity_grips.get_grip_by_id_mut(grip_id) {
                    if let Some(original) = self.original_positions.get(&(entity_id, grip_id)) {
                        grip.move_to(*original);
                    }
                    grip.end_drag();
                }
            }
        }
        
        self.dragged_grip = None;
        self.original_positions.clear();
    }
    
    pub fn update_entity_from_grips(&self, entity_id: ObjectId, entity: &mut Entity) {
        if let Some(entity_grips) = self.entity_grips.get(&entity_id) {
            self.apply_grip_modifications(entity_grips, entity);
        }
    }
    
    fn apply_grip_modifications(&self, entity_grips: &EntityGrips, entity: &mut Entity) {
        match &mut entity.geometry {
            EntityGeometry::Line(line) => {
                for grip in &entity_grips.grips {
                    match grip.grip_type {
                        GripType::Endpoint => {
                            if grip.position.distance_to(&line.start) < 1e-6 {
                                line.start = grip.position;
                            } else if grip.position.distance_to(&line.end) < 1e-6 {
                                line.end = grip.position;
                            }
                        }
                        GripType::Midpoint => {
                            let dir = line.end - line.start;
                            line.end = Point::new(
                                grip.position.x + dir.x / 2.0,
                                grip.position.y + dir.y / 2.0,
                                0.0,
                            );
                            line.start = Point::new(
                                grip.position.x - dir.x / 2.0,
                                grip.position.y - dir.y / 2.0,
                                0.0,
                            );
                        }
                        _ => {}
                    }
                }
            }
            
            EntityGeometry::Circle(circle) => {
                for grip in &entity_grips.grips {
                    match grip.grip_type {
                        GripType::Center => {
                            let delta = grip.position - circle.center;
                            circle.center = grip.position;
                        }
                        GripType::Quadrant => {
                            let new_radius = circle.center.distance_to(&grip.position);
                            circle.radius = new_radius;
                        }
                        _ => {}
                    }
                }
            }
            
            EntityGeometry::Arc(arc) => {
                for grip in &entity_grips.grips {
                    match grip.grip_type {
                        GripType::Center => {
                            arc.center = grip.position;
                        }
                        GripType::Endpoint => {
                            let new_radius = arc.center.distance_to(&grip.position);
                            arc.radius = new_radius;
                            
                            let v = (grip.position - arc.center).to_vector2();
                            let new_angle = v.angle();
                            
                            if grip.position.distance_to(&Point::new(
                                arc.center.x + arc.radius * arc.start_angle.cos(),
                                arc.center.y + arc.radius * arc.start_angle.sin(),
                                0.0,
                            )) < 1e-6 {
                                arc.start_angle = new_angle;
                            } else {
                                arc.end_angle = new_angle;
                            }
                        }
                        GripType::Midpoint => {
                            let new_radius = arc.center.distance_to(&grip.position);
                            arc.radius = new_radius;
                        }
                        _ => {}
                    }
                }
            }
            
            EntityGeometry::Polyline(polyline) => {
                for grip in &entity_grips.grips {
                    if let GripType::Vertex = grip.grip_type {
                        for vertex in &mut polyline.vertices {
                            if vertex.distance_to(&grip.position) < 1e-6 {
                                *vertex = grip.position;
                                break;
                            }
                        }
                    }
                }
            }
            
            EntityGeometry::BSpline(spline) => {
                for grip in &entity_grips.grips {
                    if let GripType::ControlPoint = grip.grip_type {
                        for point in &mut spline.control_points {
                            if point.distance_to(&grip.position) < 1e-6 {
                                *point = grip.position;
                                break;
                            }
                        }
                    }
                }
            }
            
            _ => {}
        }
    }
    
    pub fn highlight_grip(&mut self, entity_id: ObjectId, grip_id: GripId) {
        if let Some(entity_grips) = self.entity_grips.get_mut(&entity_id) {
            entity_grips.unhighlight_all();
            entity_grips.highlight_grip(grip_id);
        }
    }
    
    pub fn unhighlight_all(&mut self) {
        for entity_grips in self.entity_grips.values_mut() {
            entity_grips.unhighlight_all();
        }
    }
    
    pub fn get_entity_count(&self) -> usize {
        self.entity_grips.len()
    }
    
    pub fn get_grip_count(&self) -> usize {
        self.entity_grips.values().map(|eg| eg.grips.len()).sum()
    }
}

impl Default for GripEditor {
    fn default() -> Self {
        Self::new()
    }
}

pub struct GripMode {
    mode: GripEditMode,
    options: GripOptions,
}

#[derive(Debug, Clone, Copy, PartialEq)]
pub enum GripEditMode {
    Stretch,
    Move,
    Rotate,
    Scale,
    Mirror,
}

#[derive(Debug, Clone)]
pub struct GripOptions {
    pub base_point: Point,
    pub reference_angle: Option<f64>,
    pub scale_factor: Option<f64>,
    pub mirror_line: Option<(Point, Point)>,
}

impl GripMode {
    pub fn new() -> Self {
        Self {
            mode: GripEditMode::Stretch,
            options: GripOptions {
                base_point: Point::origin(),
                reference_angle: None,
                scale_factor: None,
                mirror_line: None,
            },
        }
    }
    
    pub fn set_mode(&mut self, mode: GripEditMode) {
        self.mode = mode;
    }
    
    pub fn get_mode(&self) -> GripEditMode {
        self.mode
    }
    
    pub fn set_base_point(&mut self, point: Point) {
        self.options.base_point = point;
    }
    
    pub fn set_reference_angle(&mut self, angle: f64) {
        self.options.reference_angle = Some(angle);
    }
    
    pub fn set_scale_factor(&mut self, factor: f64) {
        self.options.scale_factor = Some(factor);
    }
    
    pub fn set_mirror_line(&mut self, p1: Point, p2: Point) {
        self.options.mirror_line = Some((p1, p2));
    }
    
    pub fn clear_options(&mut self) {
        self.options.reference_angle = None;
        self.options.scale_factor = None;
        self.options.mirror_line = None;
    }
}

impl Default for GripMode {
    fn default() -> Self {
        Self::new()
    }
}
