use crate::editor::{ACTIONLIST, RUSTERIX};
use crate::prelude::*;
use rusterix::prelude::*;
use theframework::prelude::*;

#[derive(PartialEq, Clone, Copy, Debug)]
pub enum HudMode {
    Selection,
    Vertex,
    Linedef,
    Sector,
    Effects,
    Dungeon,
    Rect,
    Terrain,
    Entity,
}

pub struct Hud {
    mode: HudMode,

    icon_rects: Vec<TheDim>,

    pub selected_icon_index: i32,

    subdiv_rects: Vec<TheDim>,

    mouse_pos: Vec2<i32>,

    light_icon: Option<TheRGBABuffer>,

    // Plane picker widget for 3D mode
    plane_picker_rects: Vec<TheDim>,
    edit_mode_rects: Vec<TheDim>,
}

impl Hud {
    fn active_action_material_slots(
        &self,
        map: &Map,
        ctx: &mut TheContext,
        server_ctx: &mut ServerContext,
    ) -> Option<Vec<ActionMaterialSlot>> {
        if server_ctx.get_map_context() != MapContext::Region {
            return None;
        }

        if let Some(action_id) = server_ctx.curr_action_id
            && let Some(action) = ACTIONLIST.read().unwrap().get_action_by_id(action_id)
            && action.is_applicable(map, ctx, server_ctx)
            && let Some(slots) = action.hud_material_slots(map, server_ctx)
        {
            return Some(slots);
        }
        None
    }

    fn active_builder_item_slots(
        &self,
        map: &Map,
        _ctx: &mut TheContext,
        server_ctx: &mut ServerContext,
    ) -> Option<Vec<ActionItemSlot>> {
        if !server_ctx.builder_tool_active {
            return None;
        }
        match server_ctx.curr_map_tool_type {
            MapToolType::Sector => crate::actions::builder_hud_item_slots_for_selected_sector(map),
            MapToolType::Linedef => {
                crate::actions::builder_hud_item_slots_for_selected_linedef(map)
            }
            MapToolType::Vertex => crate::actions::builder_hud_item_slots_for_selected_vertex(map),
            _ => None,
        }
    }

    fn active_palette_material_slots(
        &self,
        map: &Map,
        _ctx: &mut TheContext,
        server_ctx: &mut ServerContext,
    ) -> Option<Vec<ActionMaterialSlot>> {
        if !server_ctx.palette_tool_active {
            return None;
        }
        match server_ctx.curr_map_tool_type {
            MapToolType::Sector => {
                crate::actions::builder_hud_material_slots_for_selected_sector(map)
            }
            MapToolType::Linedef => {
                crate::actions::builder_hud_material_slots_for_selected_linedef(map)
            }
            MapToolType::Vertex => {
                crate::actions::builder_hud_material_slots_for_selected_vertex(map)
            }
            _ => None,
        }
    }

    fn clean_coord(v: f32) -> f32 {
        if v.abs() < 0.0005 { 0.0 } else { v }
    }

    pub fn new(mode: HudMode) -> Self {
        Self {
            mode,

            icon_rects: vec![],
            selected_icon_index: 0,

            subdiv_rects: vec![],

            mouse_pos: Vec2::zero(),

            light_icon: None,

            plane_picker_rects: vec![],
            edit_mode_rects: vec![],
        }
    }

    pub fn draw(
        &mut self,
        buffer: &mut TheRGBABuffer,
        map: &mut Map,
        ctx: &mut TheContext,
        server_ctx: &mut ServerContext,
        id: Option<u32>,
        _assets: &Assets,
    ) {
        if (self.mode == HudMode::Linedef || self.mode == HudMode::Sector)
            && self.light_icon.is_none()
        {
            if let Some(li) = ctx.ui.icon("light_small") {
                self.light_icon = Some(li.clone());
            }
        }

        let width = buffer.dim().width as usize;
        let height = buffer.dim().height as usize;
        let stride = buffer.stride();

        let info_height = 20;
        let bg_color = [48, 48, 50, 235];
        let panel_color = [34, 34, 36, 240];
        let text_color = [168, 168, 172, 255];
        let sel_text_color = [240, 240, 242, 255];
        let accent_color = [210, 176, 92, 255];
        let geom_accent = [98, 150, 120, 255];
        let detail_accent = [176, 124, 92, 255];

        self.subdiv_rects = vec![];

        ctx.draw.rect(
            buffer.pixels_mut(),
            &(0, 0, width, info_height),
            stride,
            &bg_color,
        );
        ctx.draw.rect(
            buffer.pixels_mut(),
            &(0, info_height - 1, width, 1),
            stride,
            &[96, 96, 100, 255],
        );

        if server_ctx.editor_view_mode == EditorViewMode::D2 {
            if let Some(v) = server_ctx.hover_cursor {
                ctx.draw
                    .rect(buffer.pixels_mut(), &(8, 2, 150, 16), stride, &panel_color);
                ctx.draw.text(
                    buffer.pixels_mut(),
                    &(10, 2),
                    stride,
                    &format!("{:.2}, {:.2}", v.x, v.y),
                    TheFontSettings {
                        size: 13.0,
                        ..Default::default()
                    },
                    &text_color,
                    &bg_color,
                );
            }
        } else if let Some(v) = server_ctx.hover_cursor_3d {
            let snapped = server_ctx.snap_world_point_for_edit(map, v);
            ctx.draw
                .rect(buffer.pixels_mut(), &(8, 2, 175, 16), stride, &panel_color);
            ctx.draw.text(
                buffer.pixels_mut(),
                &(10, 2),
                stride,
                &format!(
                    "{:.2}, {:.2}, {:.2}",
                    Self::clean_coord(snapped.x),
                    Self::clean_coord(snapped.y),
                    Self::clean_coord(snapped.z)
                ),
                TheFontSettings {
                    size: 13.0,
                    ..Default::default()
                },
                &text_color,
                &bg_color,
            );
        }

        if let Some(v) = &server_ctx.background_progress {
            ctx.draw.text(
                buffer.pixels_mut(),
                &(550, 2),
                stride,
                v,
                TheFontSettings {
                    size: 13.0,
                    ..Default::default()
                },
                &text_color,
                &bg_color,
            );
        }

        // Icons

        let icon_size = 40;
        let mut icons = 0;
        let action_item_slots = self.active_builder_item_slots(map, ctx, server_ctx);
        let action_material_slots = if action_item_slots.is_none() {
            self.active_palette_material_slots(map, ctx, server_ctx)
                .or_else(|| self.active_action_material_slots(map, ctx, server_ctx))
        } else {
            None
        };

        if server_ctx.get_map_context() == MapContext::Region {
            icons = if let Some(slots) = &action_item_slots {
                slots.len() as i32
            } else if let Some(slots) = &action_material_slots {
                slots.len() as i32
            } else if self.mode == HudMode::Vertex {
                0
            } else if self.mode == HudMode::Linedef {
                0
            } else {
                1
            };
        } else if server_ctx.get_map_context() == MapContext::Screen {
            icons = if self.mode == HudMode::Sector { 2 } else { 0 };
        }

        if self.mode == HudMode::Effects
            || self.mode == HudMode::Dungeon
            || self.mode == HudMode::Rect
            || self.mode == HudMode::Terrain
        {
            icons = 0;
        }

        self.icon_rects.clear();
        let x = width as i32 - (icon_size * icons) - 1;
        for i in 0..icons {
            let rect = TheDim::rect(x + (i * icon_size), 20, icon_size, icon_size);
            ctx.draw.rect(
                buffer.pixels_mut(),
                &rect.to_buffer_utuple(),
                stride,
                &bg_color,
            );

            let r = rect.to_buffer_utuple();
            ctx.draw.text_rect(
                buffer.pixels_mut(),
                &(r.0, 1, r.2, 19),
                stride,
                &self.get_icon_text(
                    i,
                    server_ctx,
                    action_item_slots.as_deref(),
                    action_material_slots.as_deref(),
                ),
                TheFontSettings {
                    size: 10.0,
                    ..Default::default()
                },
                &text_color,
                &bg_color,
                TheHorizontalAlign::Center,
                TheVerticalAlign::Center,
            );

            let r = &rect.to_buffer_utuple();
            ctx.draw.rect(
                buffer.pixels_mut(),
                &(r.0 + 1, r.1 + 1, r.2 - 2, r.3 - 2),
                stride,
                &[30, 30, 30, 255],
            );

            if let Some(id) = id {
                let (tile, has_light) = self.get_icon(
                    i,
                    map,
                    id,
                    icon_size as usize,
                    action_item_slots.as_deref(),
                    action_material_slots.as_deref(),
                );
                if let Some(tile) = tile {
                    let texture = tile.textures[0].resized(icon_size as usize, icon_size as usize);
                    ctx.draw.blend_slice(
                        buffer.pixels_mut(),
                        &texture.data,
                        &rect.to_buffer_utuple(),
                        stride,
                    );
                }
                if has_light {
                    if let Some(light_icon) = &self.light_icon {
                        ctx.draw.blend_slice(
                            buffer.pixels_mut(),
                            light_icon.pixels(),
                            &(
                                rect.x as usize + 1,
                                rect.y as usize + 1,
                                light_icon.dim().width as usize,
                                light_icon.dim().height as usize,
                            ),
                            stride,
                        );
                    }
                }
            }

            if i == self.selected_icon_index {
                ctx.draw.rect_outline(
                    buffer.pixels_mut(),
                    &rect.to_buffer_utuple(),
                    stride,
                    &sel_text_color,
                );
            }

            self.icon_rects.push(rect);
        }

        // Show Subdivs
        if (map.camera == MapCamera::TwoD
            || server_ctx.get_map_context() == MapContext::Screen
            || server_ctx.editor_view_mode != EditorViewMode::D2)
            && self.mode != HudMode::Terrain
            && self.mode != HudMode::Dungeon
            && self.mode != HudMode::Rect
        {
            let x = 170;
            let size = 20;
            for i in 0..10 {
                let rect = TheDim::rect(x + (i * size), 0, size, size);
                let active = (i + 1) as f32 == map.subdivisions;
                let hovered = rect.contains(self.mouse_pos);
                let inner = if active {
                    [68, 68, 72, 255]
                } else if hovered {
                    [54, 54, 58, 255]
                } else {
                    panel_color
                };

                ctx.draw.rect(
                    buffer.pixels_mut(),
                    &rect.to_buffer_utuple(),
                    stride,
                    &bg_color,
                );
                ctx.draw.rect(
                    buffer.pixels_mut(),
                    &(
                        rect.x as usize + 1,
                        rect.y as usize + 2,
                        rect.width as usize - 2,
                        rect.height as usize - 4,
                    ),
                    stride,
                    &inner,
                );
                if active {
                    ctx.draw.rect(
                        buffer.pixels_mut(),
                        &(
                            rect.x as usize + 3,
                            rect.y as usize + 16,
                            rect.width as usize - 6,
                            2,
                        ),
                        stride,
                        &accent_color,
                    );
                }

                let r = rect.to_buffer_utuple();
                ctx.draw.text_rect(
                    buffer.pixels_mut(),
                    &(r.0, 1, r.2, 19),
                    stride,
                    &(i + 1).to_string(),
                    TheFontSettings {
                        size: 12.5,
                        ..Default::default()
                    },
                    &if active || hovered {
                        sel_text_color
                    } else {
                        text_color
                    },
                    &bg_color,
                    TheHorizontalAlign::Center,
                    TheVerticalAlign::Center,
                );
                self.subdiv_rects.push(rect);
            }
        }

        if server_ctx.editor_view_mode != EditorViewMode::D2 && self.mode != HudMode::Rect {
            self.edit_mode_rects.clear();
            let labels = ["GEOM", "DETAIL"];
            let modes = [GeometryEditMode::Geometry, GeometryEditMode::Detail];
            let start_x = 390;
            let button_w = 52;

            for i in 0..2 {
                let rect = TheDim::rect(start_x + (i as i32 * (button_w + 4)), 0, button_w, 20);
                let active = server_ctx.geometry_edit_mode == modes[i];
                let fg = if active { sel_text_color } else { text_color };
                let inner = if active {
                    [60, 60, 64, 255]
                } else {
                    panel_color
                };
                let accent = if i == 0 { geom_accent } else { detail_accent };

                ctx.draw.rect(
                    buffer.pixels_mut(),
                    &rect.to_buffer_utuple(),
                    stride,
                    &bg_color,
                );
                ctx.draw.rect(
                    buffer.pixels_mut(),
                    &(
                        rect.x as usize + 1,
                        rect.y as usize + 2,
                        rect.width as usize - 2,
                        rect.height as usize - 4,
                    ),
                    stride,
                    &inner,
                );
                ctx.draw.text_rect(
                    buffer.pixels_mut(),
                    &rect.to_buffer_utuple(),
                    stride,
                    labels[i],
                    TheFontSettings {
                        size: 11.0,
                        ..Default::default()
                    },
                    &fg,
                    &inner,
                    TheHorizontalAlign::Center,
                    TheVerticalAlign::Center,
                );
                if active {
                    ctx.draw.rect_outline(
                        buffer.pixels_mut(),
                        &rect.to_buffer_utuple(),
                        stride,
                        &sel_text_color,
                    );
                    ctx.draw.rect(
                        buffer.pixels_mut(),
                        &(
                            rect.x as usize + 4,
                            rect.y as usize + 16,
                            rect.width as usize - 8,
                            2,
                        ),
                        stride,
                        &accent,
                    );
                }
                self.edit_mode_rects.push(rect);
            }
        } else {
            self.edit_mode_rects.clear();
        }

        // Terrain: Height
        if self.mode == HudMode::Terrain {
            if let Some(v) = server_ctx.hover_height {
                ctx.draw.text(
                    buffer.pixels_mut(),
                    &(150, 2),
                    stride,
                    &format!("Elevation {v:.2}"),
                    TheFontSettings {
                        size: 13.0,
                        ..Default::default()
                    },
                    &text_color,
                    &bg_color,
                );
            }
        }

        // Preview

        if server_ctx.get_map_context() == MapContext::Character
            || server_ctx.get_map_context() == MapContext::Item
        {
            if let Some(Value::Texture(texture)) = map.properties.get("shape") {
                let w = texture.width as i32;
                let h = texture.height as i32;
                let preview_rect = TheDim::rect(width as i32 - w - 1, height as i32 - h - 1, w, h);
                ctx.draw.blend_slice(
                    buffer.pixels_mut(),
                    &texture.data,
                    &preview_rect.to_buffer_utuple(),
                    stride,
                );
            }
        } else if server_ctx.get_map_context() == MapContext::Screen {
            //let mut stack = ShapeStack::new(Vec2::new(-5.0, -5.0), Vec2::new(5.0, 5.0));
            //stack.render_geometry(&mut texture, map, assets, false, &FxHashMap::default());

            /*
            if let Some(Value::Texture(texture)) = map.properties.get("shape") {
                let w = texture.width as i32;
                let h = texture.height as i32;
                let preview_rect = TheDim::rect(width as i32 - w - 1, height as i32 - h - 1, w, h);
                ctx.draw.copy_slice(
                    buffer.pixels_mut(),
                    &texture.data,
                    &preview_rect.to_buffer_utuple(),
                    stride,
                );
            }*/
        }

        // Draw plane picker widget in 3D mode when editing geometry
        if server_ctx.editor_view_mode != EditorViewMode::D2
            && server_ctx.show_editing_geometry
            && server_ctx.geometry_edit_mode != GeometryEditMode::Detail
        {
            self.draw_plane_picker(buffer, ctx, server_ctx, width, height, stride);
        } else {
            self.plane_picker_rects.clear();
        }
    }

    fn draw_plane_picker(
        &mut self,
        buffer: &mut TheRGBABuffer,
        _ctx: &mut TheContext,
        server_ctx: &ServerContext,
        width: usize,
        height: usize,
        stride: usize,
    ) {
        self.plane_picker_rects.clear();

        let widget_size = 80;
        let widget_margin = 8;
        let shadow_color = [0, 0, 0, 110];

        // Position in lower-right corner
        let x = width as i32 - widget_size - widget_margin;
        let y = height as i32 - widget_size - widget_margin;

        // Draw isometric cube with three visible planes
        let cx = x + widget_size / 2;
        let cy = y + widget_size / 2 + 18;
        let size = 15;

        // Define the 8 corners of the isometric cube
        // Standard isometric projection (30-degree angles)
        let iso_x = |ix: i32, iy: i32, _iz: i32| cx + (ix - iy) * size;
        let iso_y = |ix: i32, iy: i32, iz: i32| cy + (ix + iy) * size / 2 - iz * size;

        // Cube corners (centered at origin, extending from -1 to +1 to avoid cancellation)
        let p = [
            (iso_x(-1, -1, 0), iso_y(-1, -1, 0)), // 0: bottom back left
            (iso_x(1, -1, 0), iso_y(1, -1, 0)),   // 1: bottom back right
            (iso_x(1, 1, 0), iso_y(1, 1, 0)),     // 2: bottom front right
            (iso_x(-1, 1, 0), iso_y(-1, 1, 0)),   // 3: bottom front left
            (iso_x(-1, -1, 2), iso_y(-1, -1, 2)), // 4: top back left
            (iso_x(1, -1, 2), iso_y(1, -1, 2)),   // 5: top back right
            (iso_x(1, 1, 2), iso_y(1, 1, 2)),     // 6: top front right
            (iso_x(-1, 1, 2), iso_y(-1, 1, 2)),   // 7: top front left
        ];

        // Define colors for each plane
        let xz_color = if server_ctx.gizmo_mode == GizmoMode::XZ {
            [132, 212, 148, 248]
        } else {
            [102, 112, 108, 96]
        };

        let xy_color = if server_ctx.gizmo_mode == GizmoMode::XY {
            [126, 144, 220, 248]
        } else {
            [104, 108, 122, 96]
        };

        let yz_color = if server_ctx.gizmo_mode == GizmoMode::YZ {
            [224, 146, 122, 248]
        } else {
            [122, 108, 102, 96]
        };

        // Helper to fill a quad
        let widget_bounds = (x, y, x + widget_size, y + widget_size);
        let fill_quad = |buffer: &mut [u8],
                         p0: (i32, i32),
                         p1: (i32, i32),
                         p2: (i32, i32),
                         p3: (i32, i32),
                         color: [u8; 4]| {
            // Simple scanline fill
            let points = [p0, p1, p2, p3];
            let min_y = points.iter().map(|p| p.1).min().unwrap();
            let max_y = points.iter().map(|p| p.1).max().unwrap();

            for scan_y in min_y..=max_y {
                // Skip if outside widget bounds
                if scan_y < widget_bounds.1 || scan_y >= widget_bounds.3 {
                    continue;
                }

                let mut x_intersects = Vec::new();
                for i in 0..4 {
                    let a = points[i];
                    let b = points[(i + 1) % 4];
                    if (a.1 <= scan_y && b.1 > scan_y) || (b.1 <= scan_y && a.1 > scan_y) {
                        let t = (scan_y - a.1) as f32 / (b.1 - a.1) as f32;
                        let x = a.0 as f32 + t * (b.0 - a.0) as f32;
                        x_intersects.push(x as i32);
                    }
                }
                x_intersects.sort();
                for i in (0..x_intersects.len()).step_by(2) {
                    if i + 1 < x_intersects.len() {
                        for px in x_intersects[i]..=x_intersects[i + 1] {
                            // Clip to widget bounds
                            if px >= widget_bounds.0
                                && px < widget_bounds.2
                                && scan_y >= widget_bounds.1
                                && scan_y < widget_bounds.3
                                && px >= 0
                                && scan_y >= 0
                                && (px as usize) < width
                                && (scan_y as usize) < height
                            {
                                let offset = scan_y as usize * stride * 4 + px as usize * 4;
                                if offset + 3 < buffer.len() {
                                    buffer[offset] = color[0];
                                    buffer[offset + 1] = color[1];
                                    buffer[offset + 2] = color[2];
                                    buffer[offset + 3] = color[3];
                                }
                            }
                        }
                    }
                }
            }
        };

        // Draw a soft shadow first so the gizmo reads over any scene background.
        fill_quad(
            buffer.pixels_mut(),
            (p[4].0 + 3, p[4].1 + 3),
            (p[5].0 + 3, p[5].1 + 3),
            (p[6].0 + 3, p[6].1 + 3),
            (p[7].0 + 3, p[7].1 + 3),
            shadow_color,
        );
        fill_quad(
            buffer.pixels_mut(),
            (p[3].0 + 3, p[3].1 + 3),
            (p[7].0 + 3, p[7].1 + 3),
            (p[6].0 + 3, p[6].1 + 3),
            (p[2].0 + 3, p[2].1 + 3),
            shadow_color,
        );
        fill_quad(
            buffer.pixels_mut(),
            (p[1].0 + 3, p[1].1 + 3),
            (p[5].0 + 3, p[5].1 + 3),
            (p[6].0 + 3, p[6].1 + 3),
            (p[2].0 + 3, p[2].1 + 3),
            shadow_color,
        );

        // Draw the three visible front faces (XZ top, YZ front-left, XY front-right)
        // XZ plane (top/horizontal) - points 4,5,6,7
        fill_quad(buffer.pixels_mut(), p[4], p[5], p[6], p[7], xz_color);

        // YZ plane (front-left vertical) - points 3,7,6,2
        fill_quad(buffer.pixels_mut(), p[3], p[7], p[6], p[2], yz_color);

        // XY plane (front-right vertical) - points 1,5,6,2
        fill_quad(buffer.pixels_mut(), p[1], p[5], p[6], p[2], xy_color);

        // Draw edges for clarity
        let edge_color = [196, 196, 200, 220];
        let draw_edge = |buf: &mut [u8], p0: (i32, i32), p1: (i32, i32)| {
            // Bresenham line
            let mut x0 = p0.0;
            let mut y0 = p0.1;
            let x1 = p1.0;
            let y1 = p1.1;
            let dx = (x1 - x0).abs();
            let dy = (y1 - y0).abs();
            let sx = if x0 < x1 { 1 } else { -1 };
            let sy = if y0 < y1 { 1 } else { -1 };
            let mut err = dx - dy;
            loop {
                // Clip to widget bounds
                if x0 >= widget_bounds.0
                    && x0 < widget_bounds.2
                    && y0 >= widget_bounds.1
                    && y0 < widget_bounds.3
                    && x0 >= 0
                    && y0 >= 0
                    && (x0 as usize) < width
                    && (y0 as usize) < height
                {
                    let offset = y0 as usize * stride * 4 + x0 as usize * 4;
                    if offset + 3 < buf.len() {
                        buf[offset] = edge_color[0];
                        buf[offset + 1] = edge_color[1];
                        buf[offset + 2] = edge_color[2];
                        buf[offset + 3] = edge_color[3];
                    }
                }
                if x0 == x1 && y0 == y1 {
                    break;
                }
                let e2 = 2 * err;
                if e2 > -dy {
                    err -= dy;
                    x0 += sx;
                }
                if e2 < dx {
                    err += dx;
                    y0 += sy;
                }
            }
        };

        // Draw cube edges
        let buf = buffer.pixels_mut();
        draw_edge(buf, p[4], p[5]);
        draw_edge(buf, p[5], p[6]);
        draw_edge(buf, p[6], p[7]);
        draw_edge(buf, p[7], p[4]);
        draw_edge(buf, p[1], p[5]);
        draw_edge(buf, p[2], p[6]);
        draw_edge(buf, p[3], p[7]);
        draw_edge(buf, p[1], p[2]);
        draw_edge(buf, p[2], p[3]);

        let active_edge = match server_ctx.gizmo_mode {
            GizmoMode::XZ => [248, 255, 250, 255],
            GizmoMode::YZ => [255, 244, 236, 255],
            GizmoMode::XY => [238, 244, 255, 255],
        };
        let active_face = match server_ctx.gizmo_mode {
            GizmoMode::XZ => [p[4], p[5], p[6], p[7]],
            GizmoMode::YZ => [p[3], p[7], p[6], p[2]],
            GizmoMode::XY => [p[1], p[5], p[6], p[2]],
        };
        draw_edge(buf, active_face[0], active_face[1]);
        draw_edge(buf, active_face[1], active_face[2]);
        draw_edge(buf, active_face[2], active_face[3]);
        draw_edge(buf, active_face[3], active_face[0]);
        draw_edge(buf, active_face[0], active_face[1]);
        draw_edge(buf, active_face[1], active_face[2]);
        draw_edge(buf, active_face[2], active_face[3]);
        draw_edge(buf, active_face[3], active_face[0]);
        let draw_highlight_edge = |buf: &mut [u8], p0: (i32, i32), p1: (i32, i32)| {
            let mut x0 = p0.0;
            let mut y0 = p0.1;
            let x1 = p1.0;
            let y1 = p1.1;
            let dx = (x1 - x0).abs();
            let dy = (y1 - y0).abs();
            let sx = if x0 < x1 { 1 } else { -1 };
            let sy = if y0 < y1 { 1 } else { -1 };
            let mut err = dx - dy;
            loop {
                if x0 >= widget_bounds.0
                    && x0 < widget_bounds.2
                    && y0 >= widget_bounds.1
                    && y0 < widget_bounds.3
                    && x0 >= 0
                    && y0 >= 0
                    && (x0 as usize) < width
                    && (y0 as usize) < height
                {
                    let offset = y0 as usize * stride * 4 + x0 as usize * 4;
                    if offset + 3 < buf.len() {
                        buf[offset] = active_edge[0];
                        buf[offset + 1] = active_edge[1];
                        buf[offset + 2] = active_edge[2];
                        buf[offset + 3] = active_edge[3];
                    }
                }
                if x0 == x1 && y0 == y1 {
                    break;
                }
                let e2 = 2 * err;
                if e2 > -dy {
                    err -= dy;
                    x0 += sx;
                }
                if e2 < dx {
                    err += dx;
                    y0 += sy;
                }
            }
        };
        draw_highlight_edge(buf, active_face[0], active_face[1]);
        draw_highlight_edge(buf, active_face[1], active_face[2]);
        draw_highlight_edge(buf, active_face[2], active_face[3]);
        draw_highlight_edge(buf, active_face[3], active_face[0]);

        // Store clickable regions for each plane
        // XZ plane (top)
        self.plane_picker_rects.push(TheDim::rect(
            p[4].0.min(p[5].0).min(p[6].0).min(p[7].0),
            p[4].1.min(p[5].1).min(p[6].1).min(p[7].1),
            p[4].0.max(p[5].0).max(p[6].0).max(p[7].0) - p[4].0.min(p[5].0).min(p[6].0).min(p[7].0),
            p[4].1.max(p[5].1).max(p[6].1).max(p[7].1) - p[4].1.min(p[5].1).min(p[6].1).min(p[7].1),
        ));

        // YZ plane (front-left)
        self.plane_picker_rects.push(TheDim::rect(
            p[3].0.min(p[7].0).min(p[6].0).min(p[2].0),
            p[3].1.min(p[7].1).min(p[6].1).min(p[2].1),
            p[3].0.max(p[7].0).max(p[6].0).max(p[2].0) - p[3].0.min(p[7].0).min(p[6].0).min(p[2].0),
            p[3].1.max(p[7].1).max(p[6].1).max(p[2].1) - p[3].1.min(p[7].1).min(p[6].1).min(p[2].1),
        ));

        // XY plane (front-right)
        self.plane_picker_rects.push(TheDim::rect(
            p[1].0.min(p[5].0).min(p[6].0).min(p[2].0),
            p[1].1.min(p[5].1).min(p[6].1).min(p[2].1),
            p[1].0.max(p[5].0).max(p[6].0).max(p[2].0) - p[1].0.min(p[5].0).min(p[6].0).min(p[2].0),
            p[1].1.max(p[5].1).max(p[6].1).max(p[2].1) - p[1].1.min(p[5].1).min(p[6].1).min(p[2].1),
        ));
    }

    pub fn clicked(
        &mut self,
        x: i32,
        y: i32,
        map: &mut Map,
        _ui: &mut TheUI,
        ctx: &mut TheContext,
        server_ctx: &mut ServerContext,
    ) -> bool {
        if server_ctx.get_map_context() != MapContext::Region
            && server_ctx.get_map_context() != MapContext::Screen
            && server_ctx.get_map_context() != MapContext::Character
            && server_ctx.get_map_context() != MapContext::Item
        {
            return false;
        }

        for (i, rect) in self.icon_rects.iter().enumerate() {
            if rect.contains(Vec2::new(x, y)) {
                self.selected_icon_index = i as i32;
                server_ctx.selected_hud_icon_index = i as i32;
                if self.mode == HudMode::Linedef {
                    server_ctx.selected_wall_row = Some(i as i32);
                    ctx.ui.send(TheEvent::Custom(
                        TheId::named("Map Selection Changed"),
                        TheValue::Empty,
                    ));
                }
                return true;
            }
        }
        if self.mode != HudMode::Rect {
            for (i, rect) in self.subdiv_rects.iter().enumerate() {
                if rect.contains(Vec2::new(x, y)) {
                    map.subdivisions = (i + 1) as f32;
                    return true;
                }
            }
        }

        if server_ctx.editor_view_mode != EditorViewMode::D2 {
            for (i, rect) in self.edit_mode_rects.iter().enumerate() {
                if rect.contains(Vec2::new(x, y)) {
                    server_ctx.geometry_edit_mode = if i == 0 {
                        GeometryEditMode::Geometry
                    } else {
                        GeometryEditMode::Detail
                    };
                    return true;
                }
            }
        }

        // Check plane picker clicks in 3D mode
        if server_ctx.editor_view_mode != EditorViewMode::D2
            && server_ctx.show_editing_geometry
            && server_ctx.geometry_edit_mode != GeometryEditMode::Detail
        {
            for (i, rect) in self.plane_picker_rects.iter().enumerate() {
                if rect.contains(Vec2::new(x, y)) {
                    server_ctx.gizmo_mode = match i {
                        0 => {
                            println!("Plane picker: XZ plane selected");
                            GizmoMode::XZ
                        }
                        1 => {
                            println!("Plane picker: YZ plane selected");
                            GizmoMode::YZ
                        }
                        2 => {
                            println!("Plane picker: XY plane selected");
                            GizmoMode::XY
                        }
                        _ => server_ctx.gizmo_mode,
                    };
                    return true;
                }
            }
        }

        if map.camera == MapCamera::TwoD && y < 20 {
            return true;
        }

        false
    }

    pub fn dragged(
        &mut self,
        _x: i32,
        _y: i32,
        _map: &mut Map,
        _ui: &mut TheUI,
        _ctx: &mut TheContext,
        _server_ctx: &mut ServerContext,
    ) -> bool {
        /*
        if self.timeline_rect.contains(Vec2::new(x, y)) {
            let offset = x - self.timeline_rect.x;
            let progress = offset as f32 / self.timeline_rect.width as f32;
            map.animation.transition_progress = progress;
            return true;
        }*/

        false
    }

    pub fn hovered(
        &mut self,
        x: i32,
        y: i32,
        _map: &mut Map,
        _ui: &mut TheUI,
        _ctx: &mut TheContext,
        _server_ctx: &mut ServerContext,
    ) -> bool {
        self.mouse_pos = Vec2::new(x, y);

        /*
        if self.rect_geo_rect.contains(self.mouse_pos) {
            ctx.ui.send(TheEvent::SetStatusText(
                TheId::empty(),
                "Show or hide geometry created with the Rect tool.".to_string(),
            ));
        } else {
            ctx.ui
                .send(TheEvent::SetStatusText(TheId::empty(), "".into()));
        }*/
        false
    }

    #[allow(clippy::collapsible_if)]
    pub fn get_icon_text(
        &self,
        index: i32,
        server_ctx: &mut ServerContext,
        action_item_slots: Option<&[ActionItemSlot]>,
        action_material_slots: Option<&[ActionMaterialSlot]>,
    ) -> String {
        let mut text: String = "".into();
        if let Some(slots) = action_item_slots
            && let Some(slot) = slots.get(index as usize)
        {
            return slot.label.clone();
        }
        if let Some(slots) = action_material_slots {
            if let Some(slot) = slots.get(index as usize) {
                return slot.label.clone();
            }
        }
        if server_ctx.get_map_context() == MapContext::Region {
            if self.mode == HudMode::Sector {
                if index == 0 {
                    text = "TILE".into();
                }
            }
        } else if server_ctx.get_map_context() == MapContext::Screen {
            if index == 0 {
                text = "NORM".into();
            } else if index == 1 {
                text = "ACTIVE".into();
            }
        }

        text
    }

    #[allow(clippy::collapsible_if)]
    pub fn get_icon(
        &self,
        index: i32,
        map: &Map,
        id: u32,
        icon_size: usize,
        action_item_slots: Option<&[ActionItemSlot]>,
        action_material_slots: Option<&[ActionMaterialSlot]>,
    ) -> (Option<rusterix::Tile>, bool) {
        if let Some(slots) = action_item_slots
            && slots.get(index as usize).is_some()
        {
            return (None, false);
        }
        if let Some(slots) = action_material_slots
            && let Some(slot) = slots.get(index as usize)
        {
            if let Some(pixelsource) = &slot.source {
                let props = ValueContainer::default();
                if let Some(tile) =
                    pixelsource.to_tile(&RUSTERIX.read().unwrap().assets, icon_size, &props, map)
                {
                    return (Some(tile), false);
                }
            }
            return (None, false);
        }

        if self.mode == HudMode::Sector {
            if let Some(sector) = map.find_sector(id) {
                if index == 0 {
                    let has_light = sector.properties.get("floor_light").is_some();
                    if let Some(pixelsource) = sector.properties.get_default_source() {
                        if let Some(tile) = pixelsource.to_tile(
                            &RUSTERIX.read().unwrap().assets,
                            icon_size,
                            &sector.properties,
                            map,
                        ) {
                            return (Some(tile), has_light);
                        }
                    }
                    return (None, has_light);
                } else if index == 1 {
                    let has_light = sector.properties.get("ceiling_light").is_some();
                    if let Some(Value::Source(pixelsource)) =
                        &sector.properties.get("ceiling_source")
                    {
                        if let Some(tile) = pixelsource.to_tile(
                            &RUSTERIX.read().unwrap().assets,
                            icon_size,
                            &sector.properties,
                            map,
                        ) {
                            return (Some(tile), has_light);
                        }
                    }
                    return (None, has_light);
                }
            }
        }

        (None, false)
    }
}
