use crate::ecs::text::resources::GlyphInfo;
use nalgebra_glm::{vec2, vec4};
use std::collections::HashMap;

const SDF_PADDING: usize = 8;
const SDF_SCALE: f32 = 96.0;
pub const SDF_SPREAD: f32 = 8.0;

pub struct FontAtlas {
    pub texture: wgpu::Texture,
    pub texture_view: wgpu::TextureView,
    pub texture_index: u32,
    pub glyphs: HashMap<char, GlyphInfo>,
    pub kerning: HashMap<(char, char), f32>,
    pub width: u32,
    pub height: u32,
    pub font_size: f32,
    pub sdf_range: f32,
}

#[derive(Clone, Copy, PartialEq, Eq)]
enum EdgeColor {
    Red,
    Green,
    Blue,
}

#[derive(Clone)]
enum EdgeSegment {
    Line {
        start: [f32; 2],
        end: [f32; 2],
    },
    QuadBezier {
        start: [f32; 2],
        control: [f32; 2],
        end: [f32; 2],
    },
    CubicBezier {
        start: [f32; 2],
        control1: [f32; 2],
        control2: [f32; 2],
        end: [f32; 2],
    },
}

#[derive(Clone)]
struct Edge {
    segment: EdgeSegment,
    color: EdgeColor,
}

struct Contour {
    edges: Vec<Edge>,
}

struct GlyphShape {
    contours: Vec<Contour>,
}

struct ShapeBuilder {
    contours: Vec<Contour>,
    current_edges: Vec<EdgeSegment>,
    current_point: [f32; 2],
    contour_start: [f32; 2],
}

impl ShapeBuilder {
    fn new() -> Self {
        Self {
            contours: Vec::new(),
            current_edges: Vec::new(),
            current_point: [0.0, 0.0],
            contour_start: [0.0, 0.0],
        }
    }

    fn into_shape(self) -> GlyphShape {
        GlyphShape {
            contours: self.contours,
        }
    }
}

impl ttf_parser::OutlineBuilder for ShapeBuilder {
    fn move_to(&mut self, x: f32, y: f32) {
        if !self.current_edges.is_empty() {
            let edges = std::mem::take(&mut self.current_edges)
                .into_iter()
                .map(|segment| Edge {
                    segment,
                    color: EdgeColor::Red,
                })
                .collect();
            self.contours.push(Contour { edges });
        }
        self.current_point = [x, y];
        self.contour_start = [x, y];
    }

    fn line_to(&mut self, x: f32, y: f32) {
        let start = self.current_point;
        let end = [x, y];
        if (start[0] - end[0]).abs() > f32::EPSILON || (start[1] - end[1]).abs() > f32::EPSILON {
            self.current_edges.push(EdgeSegment::Line { start, end });
        }
        self.current_point = end;
    }

    fn quad_to(&mut self, x1: f32, y1: f32, x: f32, y: f32) {
        let start = self.current_point;
        self.current_edges.push(EdgeSegment::QuadBezier {
            start,
            control: [x1, y1],
            end: [x, y],
        });
        self.current_point = [x, y];
    }

    fn curve_to(&mut self, x1: f32, y1: f32, x2: f32, y2: f32, x: f32, y: f32) {
        let start = self.current_point;
        self.current_edges.push(EdgeSegment::CubicBezier {
            start,
            control1: [x1, y1],
            control2: [x2, y2],
            end: [x, y],
        });
        self.current_point = [x, y];
    }

    fn close(&mut self) {
        let start = self.current_point;
        let end = self.contour_start;
        if (start[0] - end[0]).abs() > f32::EPSILON || (start[1] - end[1]).abs() > f32::EPSILON {
            self.current_edges.push(EdgeSegment::Line { start, end });
        }
        self.current_point = end;

        if !self.current_edges.is_empty() {
            let edges = std::mem::take(&mut self.current_edges)
                .into_iter()
                .map(|segment| Edge {
                    segment,
                    color: EdgeColor::Red,
                })
                .collect();
            self.contours.push(Contour { edges });
        }
    }
}

fn edge_direction_at_start(segment: &EdgeSegment) -> [f32; 2] {
    match segment {
        EdgeSegment::Line { start, end } => [end[0] - start[0], end[1] - start[1]],
        EdgeSegment::QuadBezier {
            start,
            control,
            end,
        } => {
            let direction = [control[0] - start[0], control[1] - start[1]];
            if direction[0].abs() > f32::EPSILON || direction[1].abs() > f32::EPSILON {
                direction
            } else {
                [end[0] - start[0], end[1] - start[1]]
            }
        }
        EdgeSegment::CubicBezier {
            start,
            control1,
            control2,
            end,
        } => {
            let direction = [control1[0] - start[0], control1[1] - start[1]];
            if direction[0].abs() > f32::EPSILON || direction[1].abs() > f32::EPSILON {
                direction
            } else {
                let direction = [control2[0] - start[0], control2[1] - start[1]];
                if direction[0].abs() > f32::EPSILON || direction[1].abs() > f32::EPSILON {
                    direction
                } else {
                    [end[0] - start[0], end[1] - start[1]]
                }
            }
        }
    }
}

fn edge_direction_at_end(segment: &EdgeSegment) -> [f32; 2] {
    match segment {
        EdgeSegment::Line { start, end } => [end[0] - start[0], end[1] - start[1]],
        EdgeSegment::QuadBezier {
            start,
            control,
            end,
        } => {
            let direction = [end[0] - control[0], end[1] - control[1]];
            if direction[0].abs() > f32::EPSILON || direction[1].abs() > f32::EPSILON {
                direction
            } else {
                [end[0] - start[0], end[1] - start[1]]
            }
        }
        EdgeSegment::CubicBezier {
            start,
            control1,
            control2,
            end,
        } => {
            let direction = [end[0] - control2[0], end[1] - control2[1]];
            if direction[0].abs() > f32::EPSILON || direction[1].abs() > f32::EPSILON {
                direction
            } else {
                let direction = [end[0] - control1[0], end[1] - control1[1]];
                if direction[0].abs() > f32::EPSILON || direction[1].abs() > f32::EPSILON {
                    direction
                } else {
                    [end[0] - start[0], end[1] - start[1]]
                }
            }
        }
    }
}

fn normalize(v: [f32; 2]) -> [f32; 2] {
    let length = (v[0] * v[0] + v[1] * v[1]).sqrt();
    if length > f32::EPSILON {
        [v[0] / length, v[1] / length]
    } else {
        [0.0, 0.0]
    }
}

fn cross_2d(a: [f32; 2], b: [f32; 2]) -> f32 {
    a[0] * b[1] - a[1] * b[0]
}

fn dot_2d(a: [f32; 2], b: [f32; 2]) -> f32 {
    a[0] * b[0] + a[1] * b[1]
}

fn color_edges(shape: &mut GlyphShape) {
    const CROSS_THRESHOLD: f32 = 0.1411;
    let colors = [EdgeColor::Red, EdgeColor::Green, EdgeColor::Blue];

    for contour in &mut shape.contours {
        if contour.edges.is_empty() {
            continue;
        }

        if contour.edges.len() == 1 {
            contour.edges[0].color = EdgeColor::Red;
            let segment_clone = contour.edges[0].segment.clone();
            contour.edges.push(Edge {
                segment: segment_clone.clone(),
                color: EdgeColor::Green,
            });
            contour.edges.push(Edge {
                segment: segment_clone,
                color: EdgeColor::Blue,
            });
            continue;
        }

        let edge_count = contour.edges.len();
        let mut corners = Vec::new();

        for edge_index in 0..edge_count {
            let prev_index = if edge_index == 0 {
                edge_count - 1
            } else {
                edge_index - 1
            };

            let prev_dir = normalize(edge_direction_at_end(&contour.edges[prev_index].segment));
            let curr_dir = normalize(edge_direction_at_start(&contour.edges[edge_index].segment));

            let cross = cross_2d(prev_dir, curr_dir);
            let dot = dot_2d(prev_dir, curr_dir);

            if dot <= 0.0 || cross.abs() > CROSS_THRESHOLD {
                corners.push(edge_index);
            }
        }

        if corners.is_empty() {
            for (color_index, edge) in contour.edges.iter_mut().enumerate() {
                edge.color = colors[color_index % 3];
            }
        } else {
            let mut color_index = 0;
            let first_corner = corners[0];

            for edge_index in 0..edge_count {
                let actual_index = (first_corner + edge_index) % edge_count;
                if corners.contains(&actual_index) && edge_index > 0 {
                    color_index += 1;
                }
                contour.edges[actual_index].color = colors[color_index % 3];
            }

            if corners.len() >= 2 {
                let last_color = contour.edges[corners[corners.len() - 1]].color;
                let first_color = contour.edges[corners[0]].color;
                if last_color == first_color {
                    let last_corner = corners[corners.len() - 1];
                    let replacement = match last_color {
                        EdgeColor::Red => EdgeColor::Blue,
                        EdgeColor::Green => EdgeColor::Red,
                        EdgeColor::Blue => EdgeColor::Green,
                    };
                    for edge_index in last_corner..edge_count {
                        contour.edges[edge_index].color = replacement;
                    }
                    for edge_index in 0..corners[0] {
                        contour.edges[edge_index].color = replacement;
                    }
                }
            }
        }
    }
}

fn signed_distance_to_line(point: [f32; 2], start: [f32; 2], end: [f32; 2]) -> f32 {
    let direction = [end[0] - start[0], end[1] - start[1]];
    let to_point = [point[0] - start[0], point[1] - start[1]];
    let length_sq = direction[0] * direction[0] + direction[1] * direction[1];

    if length_sq < f32::EPSILON {
        return (to_point[0] * to_point[0] + to_point[1] * to_point[1]).sqrt();
    }

    let parameter = (to_point[0] * direction[0] + to_point[1] * direction[1]) / length_sq;
    let clamped = parameter.clamp(0.0, 1.0);

    let closest = [
        start[0] + clamped * direction[0],
        start[1] + clamped * direction[1],
    ];
    let diff = [point[0] - closest[0], point[1] - closest[1]];
    let dist = (diff[0] * diff[0] + diff[1] * diff[1]).sqrt();

    let sign = cross_2d(direction, to_point);
    if sign < 0.0 { -dist } else { dist }
}

fn eval_quad_bezier(start: [f32; 2], control: [f32; 2], end: [f32; 2], parameter: f32) -> [f32; 2] {
    let one_minus_t = 1.0 - parameter;
    [
        one_minus_t * one_minus_t * start[0]
            + 2.0 * one_minus_t * parameter * control[0]
            + parameter * parameter * end[0],
        one_minus_t * one_minus_t * start[1]
            + 2.0 * one_minus_t * parameter * control[1]
            + parameter * parameter * end[1],
    ]
}

fn eval_quad_bezier_derivative(
    start: [f32; 2],
    control: [f32; 2],
    end: [f32; 2],
    parameter: f32,
) -> [f32; 2] {
    [
        2.0 * (1.0 - parameter) * (control[0] - start[0]) + 2.0 * parameter * (end[0] - control[0]),
        2.0 * (1.0 - parameter) * (control[1] - start[1]) + 2.0 * parameter * (end[1] - control[1]),
    ]
}

fn signed_distance_to_quad(
    point: [f32; 2],
    start: [f32; 2],
    control: [f32; 2],
    end: [f32; 2],
) -> f32 {
    let steps = 8;
    let mut best_parameter = 0.0f32;
    let mut best_dist_sq = f32::MAX;

    for step in 0..=steps {
        let parameter = step as f32 / steps as f32;
        let curve_point = eval_quad_bezier(start, control, end, parameter);
        let diff = [point[0] - curve_point[0], point[1] - curve_point[1]];
        let dist_sq = diff[0] * diff[0] + diff[1] * diff[1];
        if dist_sq < best_dist_sq {
            best_dist_sq = dist_sq;
            best_parameter = parameter;
        }
    }

    for _ in 0..4 {
        let curve_point = eval_quad_bezier(start, control, end, best_parameter);
        let derivative = eval_quad_bezier_derivative(start, control, end, best_parameter);
        let diff = [curve_point[0] - point[0], curve_point[1] - point[1]];

        let numerator = diff[0] * derivative[0] + diff[1] * derivative[1];

        let second_deriv = [
            2.0 * (end[0] - 2.0 * control[0] + start[0]),
            2.0 * (end[1] - 2.0 * control[1] + start[1]),
        ];
        let denominator = derivative[0] * derivative[0]
            + derivative[1] * derivative[1]
            + diff[0] * second_deriv[0]
            + diff[1] * second_deriv[1];

        if denominator.abs() < f32::EPSILON {
            break;
        }

        best_parameter -= numerator / denominator;
        best_parameter = best_parameter.clamp(0.0, 1.0);
    }

    let dist_at_start = {
        let diff = [point[0] - start[0], point[1] - start[1]];
        diff[0] * diff[0] + diff[1] * diff[1]
    };
    let dist_at_end = {
        let diff = [point[0] - end[0], point[1] - end[1]];
        diff[0] * diff[0] + diff[1] * diff[1]
    };
    let dist_at_best = {
        let curve_point = eval_quad_bezier(start, control, end, best_parameter);
        let diff = [point[0] - curve_point[0], point[1] - curve_point[1]];
        diff[0] * diff[0] + diff[1] * diff[1]
    };

    if dist_at_start < dist_at_best && dist_at_start < dist_at_end {
        best_parameter = 0.0;
    } else if dist_at_end < dist_at_best {
        best_parameter = 1.0;
    }

    let closest = eval_quad_bezier(start, control, end, best_parameter);
    let derivative = eval_quad_bezier_derivative(start, control, end, best_parameter);
    let diff = [point[0] - closest[0], point[1] - closest[1]];
    let dist = (diff[0] * diff[0] + diff[1] * diff[1]).sqrt();

    let sign = cross_2d(derivative, diff);
    if sign < 0.0 { -dist } else { dist }
}

fn eval_cubic_bezier(
    start: [f32; 2],
    control1: [f32; 2],
    control2: [f32; 2],
    end: [f32; 2],
    parameter: f32,
) -> [f32; 2] {
    let one_minus_t = 1.0 - parameter;
    let one_minus_t_sq = one_minus_t * one_minus_t;
    let parameter_sq = parameter * parameter;
    [
        one_minus_t_sq * one_minus_t * start[0]
            + 3.0 * one_minus_t_sq * parameter * control1[0]
            + 3.0 * one_minus_t * parameter_sq * control2[0]
            + parameter_sq * parameter * end[0],
        one_minus_t_sq * one_minus_t * start[1]
            + 3.0 * one_minus_t_sq * parameter * control1[1]
            + 3.0 * one_minus_t * parameter_sq * control2[1]
            + parameter_sq * parameter * end[1],
    ]
}

fn eval_cubic_bezier_derivative(
    start: [f32; 2],
    control1: [f32; 2],
    control2: [f32; 2],
    end: [f32; 2],
    parameter: f32,
) -> [f32; 2] {
    let one_minus_t = 1.0 - parameter;
    [
        3.0 * one_minus_t * one_minus_t * (control1[0] - start[0])
            + 6.0 * one_minus_t * parameter * (control2[0] - control1[0])
            + 3.0 * parameter * parameter * (end[0] - control2[0]),
        3.0 * one_minus_t * one_minus_t * (control1[1] - start[1])
            + 6.0 * one_minus_t * parameter * (control2[1] - control1[1])
            + 3.0 * parameter * parameter * (end[1] - control2[1]),
    ]
}

fn signed_distance_to_cubic(
    point: [f32; 2],
    start: [f32; 2],
    control1: [f32; 2],
    control2: [f32; 2],
    end: [f32; 2],
) -> f32 {
    let steps = 12;
    let mut best_parameter = 0.0f32;
    let mut best_dist_sq = f32::MAX;

    for step in 0..=steps {
        let parameter = step as f32 / steps as f32;
        let curve_point = eval_cubic_bezier(start, control1, control2, end, parameter);
        let diff = [point[0] - curve_point[0], point[1] - curve_point[1]];
        let dist_sq = diff[0] * diff[0] + diff[1] * diff[1];
        if dist_sq < best_dist_sq {
            best_dist_sq = dist_sq;
            best_parameter = parameter;
        }
    }

    for _ in 0..5 {
        let curve_point = eval_cubic_bezier(start, control1, control2, end, best_parameter);
        let derivative =
            eval_cubic_bezier_derivative(start, control1, control2, end, best_parameter);
        let diff = [curve_point[0] - point[0], curve_point[1] - point[1]];

        let numerator = diff[0] * derivative[0] + diff[1] * derivative[1];

        let one_minus_t = 1.0 - best_parameter;
        let second_deriv = [
            6.0 * one_minus_t * (control2[0] - 2.0 * control1[0] + start[0])
                + 6.0 * best_parameter * (end[0] - 2.0 * control2[0] + control1[0]),
            6.0 * one_minus_t * (control2[1] - 2.0 * control1[1] + start[1])
                + 6.0 * best_parameter * (end[1] - 2.0 * control2[1] + control1[1]),
        ];
        let denominator = derivative[0] * derivative[0]
            + derivative[1] * derivative[1]
            + diff[0] * second_deriv[0]
            + diff[1] * second_deriv[1];

        if denominator.abs() < f32::EPSILON {
            break;
        }

        best_parameter -= numerator / denominator;
        best_parameter = best_parameter.clamp(0.0, 1.0);
    }

    let dist_at_start = {
        let diff = [point[0] - start[0], point[1] - start[1]];
        diff[0] * diff[0] + diff[1] * diff[1]
    };
    let dist_at_end = {
        let diff = [point[0] - end[0], point[1] - end[1]];
        diff[0] * diff[0] + diff[1] * diff[1]
    };
    let dist_at_best = {
        let curve_point = eval_cubic_bezier(start, control1, control2, end, best_parameter);
        let diff = [point[0] - curve_point[0], point[1] - curve_point[1]];
        diff[0] * diff[0] + diff[1] * diff[1]
    };

    if dist_at_start < dist_at_best && dist_at_start < dist_at_end {
        best_parameter = 0.0;
    } else if dist_at_end < dist_at_best {
        best_parameter = 1.0;
    }

    let closest = eval_cubic_bezier(start, control1, control2, end, best_parameter);
    let derivative = eval_cubic_bezier_derivative(start, control1, control2, end, best_parameter);
    let diff = [point[0] - closest[0], point[1] - closest[1]];
    let dist = (diff[0] * diff[0] + diff[1] * diff[1]).sqrt();

    let sign = cross_2d(derivative, diff);
    if sign < 0.0 { -dist } else { dist }
}

fn signed_distance_to_edge(point: [f32; 2], segment: &EdgeSegment) -> f32 {
    match segment {
        EdgeSegment::Line { start, end } => signed_distance_to_line(point, *start, *end),
        EdgeSegment::QuadBezier {
            start,
            control,
            end,
        } => signed_distance_to_quad(point, *start, *control, *end),
        EdgeSegment::CubicBezier {
            start,
            control1,
            control2,
            end,
        } => signed_distance_to_cubic(point, *start, *control1, *control2, *end),
    }
}

fn line_winding(point: [f32; 2], start: [f32; 2], end: [f32; 2]) -> i32 {
    if (start[1] <= point[1]) != (end[1] <= point[1]) {
        let parameter = (point[1] - start[1]) / (end[1] - start[1]);
        let x_at_crossing = start[0] + parameter * (end[0] - start[0]);
        if x_at_crossing > point[0] {
            if end[1] > start[1] {
                return 1;
            } else {
                return -1;
            }
        }
    }
    0
}

fn quad_winding(point: [f32; 2], start: [f32; 2], control: [f32; 2], end: [f32; 2]) -> i32 {
    let a = start[1] - 2.0 * control[1] + end[1];
    let b = 2.0 * (control[1] - start[1]);
    let c = start[1] - point[1];

    let mut winding = 0i32;

    if a.abs() < f32::EPSILON {
        if b.abs() < f32::EPSILON {
            return 0;
        }
        let parameter = -c / b;
        if parameter > 0.0 && parameter <= 1.0 {
            let x = eval_quad_bezier(start, control, end, parameter)[0];
            if x > point[0] {
                let dy = b + 2.0 * a * parameter;
                if dy > 0.0 {
                    winding += 1;
                } else if dy < 0.0 {
                    winding -= 1;
                }
            }
        }
        return winding;
    }

    let discriminant = b * b - 4.0 * a * c;
    if discriminant < 0.0 {
        return 0;
    }

    let sqrt_disc = discriminant.sqrt();
    let roots = [(-b - sqrt_disc) / (2.0 * a), (-b + sqrt_disc) / (2.0 * a)];

    for parameter in roots {
        if parameter > 0.0 && parameter <= 1.0 {
            let x = eval_quad_bezier(start, control, end, parameter)[0];
            if x > point[0] {
                let dy = b + 2.0 * a * parameter;
                if dy > 0.0 {
                    winding += 1;
                } else if dy < 0.0 {
                    winding -= 1;
                }
            }
        }
    }

    winding
}

fn cubic_winding(
    point: [f32; 2],
    start: [f32; 2],
    control1: [f32; 2],
    control2: [f32; 2],
    end: [f32; 2],
) -> i32 {
    let a = -start[1] + 3.0 * control1[1] - 3.0 * control2[1] + end[1];
    let b = 3.0 * (start[1] - 2.0 * control1[1] + control2[1]);
    let c = 3.0 * (control1[1] - start[1]);
    let d = start[1] - point[1];

    let (roots, root_count) = solve_cubic(a, b, c, d);
    let mut winding = 0i32;

    for &parameter in &roots[..root_count] {
        if parameter > 0.0 && parameter <= 1.0 {
            let curve_point = eval_cubic_bezier(start, control1, control2, end, parameter);
            if curve_point[0] > point[0] {
                let dy = eval_cubic_bezier_derivative(start, control1, control2, end, parameter)[1];
                if dy > 0.0 {
                    winding += 1;
                } else if dy < 0.0 {
                    winding -= 1;
                }
            }
        }
    }

    winding
}

fn solve_cubic(a: f32, b: f32, c: f32, d: f32) -> ([f32; 3], usize) {
    if a.abs() < f32::EPSILON {
        if b.abs() < f32::EPSILON {
            if c.abs() < f32::EPSILON {
                return ([0.0; 3], 0);
            }
            return ([-d / c, 0.0, 0.0], 1);
        }
        let disc = c * c - 4.0 * b * d;
        if disc < 0.0 {
            return ([0.0; 3], 0);
        }
        let sqrt_disc = disc.sqrt();
        return (
            [
                (-c - sqrt_disc) / (2.0 * b),
                (-c + sqrt_disc) / (2.0 * b),
                0.0,
            ],
            2,
        );
    }

    let ba = b / a;
    let ca = c / a;
    let da = d / a;

    let p = (3.0 * ca - ba * ba) / 3.0;
    let q = (2.0 * ba * ba * ba - 9.0 * ba * ca + 27.0 * da) / 27.0;
    let discriminant = q * q / 4.0 + p * p * p / 27.0;

    let offset = -ba / 3.0;

    if discriminant > f32::EPSILON {
        let sqrt_disc = discriminant.sqrt();
        let u = (-q / 2.0 + sqrt_disc).cbrt();
        let v = (-q / 2.0 - sqrt_disc).cbrt();
        ([u + v + offset, 0.0, 0.0], 1)
    } else if discriminant.abs() <= f32::EPSILON {
        let u = if q.abs() < f32::EPSILON {
            0.0
        } else {
            (-q / 2.0).cbrt()
        };
        ([2.0 * u + offset, -u + offset, 0.0], 2)
    } else {
        let r = (-p * p * p / 27.0).sqrt();
        let phi = (-q / (2.0 * r)).clamp(-1.0, 1.0).acos();
        let cube_root_r = r.cbrt();
        (
            [
                2.0 * cube_root_r * (phi / 3.0).cos() + offset,
                2.0 * cube_root_r * ((phi + 2.0 * std::f32::consts::PI) / 3.0).cos() + offset,
                2.0 * cube_root_r * ((phi + 4.0 * std::f32::consts::PI) / 3.0).cos() + offset,
            ],
            3,
        )
    }
}

fn edge_winding(point: [f32; 2], segment: &EdgeSegment) -> i32 {
    match segment {
        EdgeSegment::Line { start, end } => line_winding(point, *start, *end),
        EdgeSegment::QuadBezier {
            start,
            control,
            end,
        } => quad_winding(point, *start, *control, *end),
        EdgeSegment::CubicBezier {
            start,
            control1,
            control2,
            end,
        } => cubic_winding(point, *start, *control1, *control2, *end),
    }
}

fn compute_winding_number(point: [f32; 2], shape: &GlyphShape) -> i32 {
    let mut winding = 0i32;
    for contour in &shape.contours {
        for edge in &contour.edges {
            winding += edge_winding(point, &edge.segment);
        }
    }
    winding
}

fn compute_msdf_pixel(point: [f32; 2], shape: &GlyphShape) -> [f32; 3] {
    let winding = compute_winding_number(point, shape);
    let sign = if winding != 0 { 1.0f32 } else { -1.0f32 };

    let mut min_red = f32::MAX;
    let mut min_green = f32::MAX;
    let mut min_blue = f32::MAX;

    for contour in &shape.contours {
        for edge in &contour.edges {
            let abs_dist = signed_distance_to_edge(point, &edge.segment).abs();
            match edge.color {
                EdgeColor::Red => {
                    if abs_dist < min_red {
                        min_red = abs_dist;
                    }
                }
                EdgeColor::Green => {
                    if abs_dist < min_green {
                        min_green = abs_dist;
                    }
                }
                EdgeColor::Blue => {
                    if abs_dist < min_blue {
                        min_blue = abs_dist;
                    }
                }
            }
        }
    }

    [sign * min_red, sign * min_green, sign * min_blue]
}

fn generate_msdf_bitmap(
    shape: &GlyphShape,
    width: usize,
    height: usize,
    translate_x: f32,
    translate_y: f32,
    scale: f32,
    spread: f32,
) -> Vec<[u8; 4]> {
    let mut pixels = vec![[0u8; 4]; width * height];

    for pixel_y in 0..height {
        for pixel_x in 0..width {
            let shape_x = (pixel_x as f32 - translate_x) / scale;
            let shape_y = ((height - 1 - pixel_y) as f32 - translate_y) / scale;

            let distances = compute_msdf_pixel([shape_x, shape_y], shape);

            let pixel_index = pixel_y * width + pixel_x;
            pixels[pixel_index] = [
                ((0.5 + distances[0] / spread).clamp(0.0, 1.0) * 255.0) as u8,
                ((0.5 + distances[1] / spread).clamp(0.0, 1.0) * 255.0) as u8,
                ((0.5 + distances[2] / spread).clamp(0.0, 1.0) * 255.0) as u8,
                255,
            ];
        }
    }

    pixels
}

pub struct AtlasPacker {
    width: u32,
    height: u32,
    current_x: u32,
    current_y: u32,
    row_height: u32,
}

impl AtlasPacker {
    pub fn new(width: u32, height: u32) -> Self {
        Self {
            width,
            height,
            current_x: 0,
            current_y: 0,
            row_height: 0,
        }
    }

    pub fn pack(&mut self, glyph_width: u32, glyph_height: u32) -> Option<(u32, u32)> {
        if self.current_x + glyph_width > self.width {
            self.current_x = 0;
            self.current_y += self.row_height;
            self.row_height = 0;
        }

        if self.current_y + glyph_height > self.height {
            return None;
        }

        let position = (self.current_x, self.current_y);
        self.current_x += glyph_width;
        self.row_height = self.row_height.max(glyph_height);

        Some(position)
    }
}

pub fn generate_font_atlas_msdf(
    device: &wgpu::Device,
    queue: &wgpu::Queue,
    font_data: &[u8],
    font_size: f32,
) -> Result<FontAtlas, Box<dyn std::error::Error>> {
    let face = ttf_parser::Face::parse(font_data, 0)?;

    let units_per_em = face.units_per_em() as f32;
    let scale = SDF_SCALE / units_per_em;
    let spread = SDF_SPREAD / scale;

    let atlas_width: u32 = 2048;
    let atlas_height: u32 = 2048;
    let mut atlas_data = vec![[0u8; 4]; (atlas_width * atlas_height) as usize];
    let mut packer = AtlasPacker::new(atlas_width, atlas_height);
    let mut glyphs = HashMap::new();

    let chars_to_render: Vec<char> = (32u8..127u8).map(|c| c as char).collect();

    for &ch in &chars_to_render {
        if let Some(glyph_id) = face.glyph_index(ch) {
            let mut builder = ShapeBuilder::new();
            let bbox = face.outline_glyph(glyph_id, &mut builder);

            if let Some(bbox) = bbox {
                let mut shape = builder.into_shape();

                if shape.contours.is_empty() {
                    if ch == ' ' {
                        let advance = face.glyph_hor_advance(glyph_id).unwrap_or(0) as f32;
                        let scale_factor = font_size / units_per_em;
                        glyphs.insert(
                            ch,
                            GlyphInfo {
                                uv_rect: vec4(0.0, 0.0, 0.0, 0.0),
                                size: vec2(0.0, 0.0),
                                bearing: vec2(0.0, 0.0),
                                advance: advance * scale_factor,
                            },
                        );
                    }
                    continue;
                }

                color_edges(&mut shape);

                let glyph_width = (bbox.x_max - bbox.x_min) as f32;
                let glyph_height = (bbox.y_max - bbox.y_min) as f32;

                let pixel_width = (glyph_width * scale).ceil() as usize + SDF_PADDING * 2;
                let pixel_height = (glyph_height * scale).ceil() as usize + SDF_PADDING * 2;

                if let Some((atlas_x, atlas_y)) =
                    packer.pack(pixel_width as u32, pixel_height as u32)
                {
                    let translate_x = SDF_PADDING as f32 - bbox.x_min as f32 * scale;
                    let translate_y = SDF_PADDING as f32 - bbox.y_min as f32 * scale;

                    let msdf_bitmap = generate_msdf_bitmap(
                        &shape,
                        pixel_width,
                        pixel_height,
                        translate_x,
                        translate_y,
                        scale,
                        spread,
                    );

                    for src_y in 0..pixel_height {
                        for src_x in 0..pixel_width {
                            let src_idx = src_y * pixel_width + src_x;
                            let dst_x = atlas_x as usize + src_x;
                            let dst_y = atlas_y as usize + src_y;
                            let dst_idx = dst_y * atlas_width as usize + dst_x;
                            if dst_idx < atlas_data.len() {
                                atlas_data[dst_idx] = msdf_bitmap[src_idx];
                            }
                        }
                    }

                    let scale_factor = font_size / units_per_em;
                    let advance = face.glyph_hor_advance(glyph_id).unwrap_or(0) as f32;

                    glyphs.insert(
                        ch,
                        GlyphInfo {
                            uv_rect: vec4(
                                atlas_x as f32 / atlas_width as f32,
                                atlas_y as f32 / atlas_height as f32,
                                pixel_width as f32 / atlas_width as f32,
                                pixel_height as f32 / atlas_height as f32,
                            ),
                            size: vec2(
                                pixel_width as f32 * (font_size / SDF_SCALE),
                                pixel_height as f32 * (font_size / SDF_SCALE),
                            ),
                            bearing: vec2(
                                (bbox.x_min as f32 * scale_factor)
                                    - (SDF_PADDING as f32 * font_size / SDF_SCALE),
                                (bbox.y_max as f32 * scale_factor)
                                    + (SDF_PADDING as f32 * font_size / SDF_SCALE),
                            ),
                            advance: advance * scale_factor,
                        },
                    );
                }
            } else if ch == ' ' {
                let advance = face.glyph_hor_advance(glyph_id).unwrap_or(0) as f32;
                let scale_factor = font_size / units_per_em;
                glyphs.insert(
                    ch,
                    GlyphInfo {
                        uv_rect: vec4(0.0, 0.0, 0.0, 0.0),
                        size: vec2(0.0, 0.0),
                        bearing: vec2(0.0, 0.0),
                        advance: advance * scale_factor,
                    },
                );
            }
        }
    }

    let mut kerning = HashMap::new();
    let scale_factor = font_size / units_per_em;
    if let Some(kern_table) = face.tables().kern {
        for &left_char in &chars_to_render {
            for &right_char in &chars_to_render {
                if let Some(left_id) = face.glyph_index(left_char)
                    && let Some(right_id) = face.glyph_index(right_char)
                {
                    let kern_value: i16 = kern_table
                        .subtables
                        .into_iter()
                        .filter(|subtable| subtable.horizontal && !subtable.variable)
                        .find_map(|subtable| subtable.glyphs_kerning(left_id, right_id))
                        .unwrap_or(0);
                    if kern_value != 0 {
                        kerning.insert((left_char, right_char), kern_value as f32 * scale_factor);
                    }
                }
            }
        }
    }

    let flat_data: Vec<u8> = atlas_data
        .iter()
        .flat_map(|pixel| pixel.iter().copied())
        .collect();

    let size = wgpu::Extent3d {
        width: atlas_width,
        height: atlas_height,
        depth_or_array_layers: 1,
    };

    let texture = device.create_texture(&wgpu::TextureDescriptor {
        label: Some("Font Atlas MSDF"),
        size,
        mip_level_count: 1,
        sample_count: 1,
        dimension: wgpu::TextureDimension::D2,
        format: wgpu::TextureFormat::Rgba8Unorm,
        usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
        view_formats: &[],
    });

    queue.write_texture(
        wgpu::TexelCopyTextureInfo {
            aspect: wgpu::TextureAspect::All,
            texture: &texture,
            mip_level: 0,
            origin: wgpu::Origin3d::ZERO,
        },
        &flat_data,
        wgpu::TexelCopyBufferLayout {
            offset: 0,
            bytes_per_row: Some(atlas_width * 4),
            rows_per_image: Some(atlas_height),
        },
        size,
    );

    let texture_view = texture.create_view(&wgpu::TextureViewDescriptor::default());

    Ok(FontAtlas {
        texture,
        texture_view,
        texture_index: 0,
        glyphs,
        kerning,
        width: atlas_width,
        height: atlas_height,
        font_size,
        sdf_range: SDF_SPREAD,
    })
}

pub fn generate_font_atlas_bitmap(
    device: &wgpu::Device,
    queue: &wgpu::Queue,
    font_data: &[u8],
    font_size: f32,
    scale_factor: f32,
    subpixel: bool,
) -> Result<FontAtlas, Box<dyn std::error::Error>> {
    use swash::FontRef;
    use swash::scale::{Render, ScaleContext, Source};
    use swash::zeno::Format;

    let font_ref = FontRef::from_index(font_data, 0).ok_or("Failed to parse font")?;
    let mut context = ScaleContext::new();
    let charmap = font_ref.charmap();
    let glyph_metrics = font_ref.glyph_metrics(&[]).scale(font_size * scale_factor);

    let raster_size = font_size * scale_factor;
    let base_dim = if raster_size <= 24.0 { 512 } else { 1024 };
    let atlas_dim = ((base_dim as f32 * scale_factor).ceil() as u32).min(4096);
    let atlas_width: u32 = atlas_dim;
    let atlas_height: u32 = atlas_dim;
    let mut atlas_data = vec![0u8; (atlas_width * atlas_height * 4) as usize];
    let mut packer = AtlasPacker::new(atlas_width, atlas_height);
    let mut glyphs = HashMap::new();

    let chars_to_render: Vec<char> = (32u8..127u8).map(|character| character as char).collect();

    let format = if subpixel {
        Format::Subpixel
    } else {
        Format::Alpha
    };

    let mut scaler = context
        .builder(font_ref)
        .size(raster_size)
        .hint(true)
        .build();

    for &character in &chars_to_render {
        let glyph_id = charmap.map(character);
        let advance = glyph_metrics.advance_width(glyph_id);

        let image = Render::new(&[Source::ColorOutline(0), Source::Outline])
            .format(format)
            .render(&mut scaler, glyph_id);

        let Some(image) = image else {
            glyphs.insert(
                character,
                GlyphInfo {
                    uv_rect: vec4(0.0, 0.0, 0.0, 0.0),
                    size: vec2(0.0, 0.0),
                    bearing: vec2(0.0, 0.0),
                    advance,
                },
            );
            continue;
        };

        let glyph_width = image.placement.width;
        let glyph_height = image.placement.height;

        if glyph_width == 0 || glyph_height == 0 {
            glyphs.insert(
                character,
                GlyphInfo {
                    uv_rect: vec4(0.0, 0.0, 0.0, 0.0),
                    size: vec2(0.0, 0.0),
                    bearing: vec2(0.0, 0.0),
                    advance,
                },
            );
            continue;
        }

        let Some((pack_x, pack_y)) = packer.pack(glyph_width + 1, glyph_height + 1) else {
            continue;
        };

        for row in 0..glyph_height as usize {
            for col in 0..glyph_width as usize {
                let destination_x = pack_x as usize + col;
                let destination_y = pack_y as usize + row;
                let destination_index = (destination_y * atlas_width as usize + destination_x) * 4;

                if subpixel {
                    let source_index = (row * glyph_width as usize + col) * 4;
                    let red = image.data[source_index];
                    let green = image.data[source_index + 1];
                    let blue = image.data[source_index + 2];
                    atlas_data[destination_index] = red;
                    atlas_data[destination_index + 1] = green;
                    atlas_data[destination_index + 2] = blue;
                    atlas_data[destination_index + 3] = red.max(green).max(blue);
                } else {
                    let source_index = row * glyph_width as usize + col;
                    let coverage = image.data[source_index];
                    atlas_data[destination_index] = 255;
                    atlas_data[destination_index + 1] = 255;
                    atlas_data[destination_index + 2] = 255;
                    atlas_data[destination_index + 3] = coverage;
                }
            }
        }

        glyphs.insert(
            character,
            GlyphInfo {
                uv_rect: vec4(
                    pack_x as f32 / atlas_width as f32,
                    pack_y as f32 / atlas_height as f32,
                    glyph_width as f32 / atlas_width as f32,
                    glyph_height as f32 / atlas_height as f32,
                ),
                size: vec2(glyph_width as f32, glyph_height as f32),
                bearing: vec2(image.placement.left as f32, image.placement.top as f32),
                advance,
            },
        );
    }

    let face = ttf_parser::Face::parse(font_data, 0)?;
    let units_per_em = face.units_per_em() as f32;
    let kern_scale = raster_size / units_per_em;
    let mut kerning = HashMap::new();
    if let Some(kern_table) = face.tables().kern {
        for &left_char in &chars_to_render {
            for &right_char in &chars_to_render {
                if let Some(left_id) = face.glyph_index(left_char)
                    && let Some(right_id) = face.glyph_index(right_char)
                {
                    let kern_value: i16 = kern_table
                        .subtables
                        .into_iter()
                        .filter(|subtable| subtable.horizontal && !subtable.variable)
                        .find_map(|subtable| subtable.glyphs_kerning(left_id, right_id))
                        .unwrap_or(0);
                    if kern_value != 0 {
                        kerning.insert((left_char, right_char), kern_value as f32 * kern_scale);
                    }
                }
            }
        }
    }

    let size = wgpu::Extent3d {
        width: atlas_width,
        height: atlas_height,
        depth_or_array_layers: 1,
    };

    let texture = device.create_texture(&wgpu::TextureDescriptor {
        label: Some("Font Atlas Bitmap"),
        size,
        mip_level_count: 1,
        sample_count: 1,
        dimension: wgpu::TextureDimension::D2,
        format: wgpu::TextureFormat::Rgba8Unorm,
        usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
        view_formats: &[],
    });

    queue.write_texture(
        wgpu::TexelCopyTextureInfo {
            aspect: wgpu::TextureAspect::All,
            texture: &texture,
            mip_level: 0,
            origin: wgpu::Origin3d::ZERO,
        },
        &atlas_data,
        wgpu::TexelCopyBufferLayout {
            offset: 0,
            bytes_per_row: Some(atlas_width * 4),
            rows_per_image: Some(atlas_height),
        },
        size,
    );

    let texture_view = texture.create_view(&wgpu::TextureViewDescriptor::default());

    Ok(FontAtlas {
        texture,
        texture_view,
        texture_index: 0,
        glyphs,
        kerning,
        width: atlas_width,
        height: atlas_height,
        font_size: raster_size,
        sdf_range: 0.0,
    })
}

pub const DEFAULT_FONT_DATA: &[u8] = include_bytes!("fonts/Roboto-Regular.ttf");
