use potlood::{Color, Paper, Pixel};

#[derive(Clone, Copy)]
struct Point {
    x: f64,
    y: f64,
}

impl Point {
    fn new(x: f64, y: f64) -> Self {
        Self { x, y }
    }
}

fn lerp(a: f64, b: f64, t: f64) -> f64 {
    a + t * (b - a)
}

fn lerp_points(a: Point, b: Point, t: f64) -> Point {
    let x = lerp(a.x, b.x, t);
    let y = lerp(a.y, b.y, t);
    Point::new(x, y)
}

fn bezier(
    start: Point,
    start_handle: Point,
    end: Point,
    end_handle: Point,
    steps: usize,
) -> Vec<Point> {
    let mut curve = Vec::with_capacity(steps);
    for step in 0..=steps {
        let t = step as f64 / steps as f64;
        let m = lerp_points(start_handle, end_handle, t);
        let s = lerp_points(start, start_handle, t);
        let e = lerp_points(end_handle, end, t);
        let sm = lerp_points(s, m, t);
        let me = lerp_points(m, e, t);
        let point = lerp_points(sm, me, t);
        curve.push(point);
    }

    curve
}

fn main() {
    let width = 400;
    let height = 300;
    let padding = 20.0;
    let mut paper = Paper::new(width, height);

    // TODO: Is this necessary?
    paper.fill(Pixel::color(Color::Black));

    let start = Point::new(padding, 10.0);
    let start_handle = Point::new(padding, height as f64 - 10.0);
    let end = Point::new(width as f64 - padding, height as f64 - 10.0);
    let end_handle = Point::new(width as f64 - padding, 10.0);

    for step in 0..=20 {
        let t = step as f64 / 20 as f64;
        let m = lerp_points(start_handle, end_handle, t);
        let s = lerp_points(start, start_handle, t);
        let e = lerp_points(end_handle, end, t);
        let sm = lerp_points(s, m, t);
        let me = lerp_points(m, e, t);
        paper.line(
            sm.x as usize,
            sm.y as usize,
            me.x as usize,
            me.y as usize,
            Pixel::color(Color::Grey),
        );
    }
    paper.stroke(
        bezier(start, start_handle, end, end_handle, 50)
            .iter()
            .map(|point| (point.x as usize, point.y as usize))
            .collect(),
        Pixel::color(Color::Red),
    );
    for point in bezier(start, start_handle, end, end_handle, 50) {
        paper.set(
            point.x as usize,
            point.y as usize,
            Pixel::color(Color::Magenta),
        );
    }
    for point in bezier(start, start_handle, end, end_handle, 25) {
        paper.set(
            point.x as usize,
            point.y as usize,
            Pixel::color(Color::White),
        );
    }

    let mut draw_point = |p: Point, color| {
        paper
            .circle(p.x as usize, p.y as usize, 2, Pixel::color(color))
            .unwrap()
    };
    draw_point(start, Color::Blue);
    draw_point(start_handle, Color::Green);
    draw_point(end, Color::Blue);
    draw_point(end_handle, Color::Green);

    paper
        .generate("./bezier.ppm".to_string())
        .expect("something went wrong");
}
