use crate::tables::glyf::{CompositeComponent, Glyph, ProtoGlyph, SimpleGlyph, ARGS_ARE_XY_VALUES, WE_HAVE_AN_X_AND_Y_SCALE, WE_HAVE_A_SCALE, WE_HAVE_A_TWO_BY_TWO};

use crate::font::TrueTypeFont;
use crate::Vec;

#[derive(Debug, Copy, Clone)]
pub(crate) struct Point {
    pub(crate) x: f32,
    pub(crate) y: f32,
    pub(crate) on_curve: bool,
}

#[derive(Debug, Clone)]
pub(crate) struct Contour {
    pub(crate) points: Vec<Point>,
}

impl Contour {
    pub(crate) fn new(size: usize) -> Self {
        Contour { points: Vec::with_capacity(size) }
    }
}

impl TrueTypeFont {
    pub(crate) fn load_points(&self, glyph: &mut ProtoGlyph, font: &TrueTypeFont, font_bytes: &[u8]) -> Glyph {
        match glyph {
            ProtoGlyph::Simple(g) => {
                load_simple_glyph(g, None);
            }

            ProtoGlyph::Composite(g) => {
                load_from_parent(&mut g.points, &g.components, font, font_bytes);
            }

            ProtoGlyph::Empty => {}
        }

        let glyph = glyph.finalize();
        //glyph.build_lines(self.head.units_per_em as f32, 40.0);

        glyph
    }
}

pub(crate) fn load_from_parent(master: &mut Vec<Contour>, comps: &Vec<CompositeComponent>, font: &TrueTypeFont, font_bytes: &[u8]) {
    for component in comps.iter() {
        let real_glyph = &mut font.get_glyph(font_bytes, component.glyph_index as u32);

        match real_glyph {
            ProtoGlyph::Simple(g) => {
                load_simple_glyph(g, Some(component));
                master.extend(g.points.iter().cloned());
            }

            ProtoGlyph::Composite(g) => {
                load_from_parent(master, &g.components, font, font_bytes);
            }

            ProtoGlyph::Empty => {}
        }
    }
}

pub fn load_simple_glyph(g: &mut SimpleGlyph, component: Option<&CompositeComponent>) {
    if !g.points.is_empty() {
        return;
    }

    let end_pts = &g.end_pts_of_contours;
    let num_points = end_pts.last().map(|&e| (e + 1) as usize).unwrap_or(0);
    
    // g.flags is already expanded in font.rs, so we can just use it directly.
    // The 0x01 bit indicates if the point is on the curve.
    if g.flags.len() < num_points {
        // Should not happen if font.rs is correct, but safety first.
        return;
    }

    g.points.reserve(end_pts.len());

    let mut contour_start = 0;
    for &end_pt in end_pts {
        let end_pt = end_pt as usize;
        let contour_size = end_pt - contour_start + 1;
        let mut contour = Contour::new(contour_size);

        for j in contour_start..=end_pt {
            contour.points.push(Point {
                x: g.x_coordinates[j] as f32,
                y: g.y_coordinates[j] as f32,
                on_curve: (g.flags[j] & 0x01) != 0,
            });
        }

        if let Some(ref component) = component {
            transform_points(&mut contour.points, component);
        }

        contour_start = end_pt + 1;
        g.points.push(contour);
    }
}


fn transform_points(points: &mut [Point], component: &CompositeComponent) {
    let (x_scale, y_scale, scale_01, scale_10) = if component.flags & WE_HAVE_A_TWO_BY_TWO != 0 {
        (
            component.x_scale.unwrap_or(1.0),
            component.y_scale.unwrap_or(1.0),
            component.scale_01.unwrap_or(0.0),
            component.scale_10.unwrap_or(0.0),
        )
    } else if component.flags & WE_HAVE_AN_X_AND_Y_SCALE != 0 {
        (component.x_scale.unwrap_or(1.0), component.y_scale.unwrap_or(1.0), 0.0, 0.0)
    } else if component.flags & WE_HAVE_A_SCALE != 0 {
        let s = component.scale.unwrap_or(1.0);
        (s, s, 0.0, 0.0)
    } else {
        (1.0, 1.0, 0.0, 0.0)
    };

    for p in points.iter_mut() {
        let old_x = p.x;
        let old_y = p.y;
        let nx = old_x * x_scale + old_y * scale_10;
        let ny = old_x * scale_01 + old_y * y_scale;
        p.x = nx;
        p.y = ny;
    }

        if component.flags & ARGS_ARE_XY_VALUES != 0 {

            let dx = component.argument1 as f32;

            let dy = component.argument2 as f32;

            for p in points.iter_mut() {

                p.x = p.x + dx;

                p.y = p.y + dy;

            }

        }

    }

    