use std::f64::consts::PI;

use ranim::{
    anims::rotating::RotatingAnim,
    color::palettes::manim,
    core::animation::StaticAnim,
    glam::{DVec3, dvec2, dvec3},
    items::{
        debug::VisualizeAabbItem,
        vitem::{
            VItem,
            geometry::{
                Arc, ArcBetweenPoints, Circle, Ellipse, EllipticArc, Polygon, Rectangle,
                RegularPolygon, Square,
            },
        },
    },
    prelude::*,
};

#[scene]
#[output(dir = "./output/aabb")]
fn aabb(r: &mut RanimScene) {
    let _r_cam = r.insert(CameraFrame::default());

    let frame_w = 8.0 * 16.0 / 9.0;
    let frame_h = 8.0;
    let cols = 4;
    let rows = 4;
    let cell_w = frame_w / cols as f64;
    let cell_h = frame_h / rows as f64;
    let target = cell_h * 0.6;

    let cell_center = |col: usize, row: usize| -> DVec3 {
        let x = (col as f64 + 0.5) * cell_w - frame_w / 2.0;
        let y = (rows - 1 - row) as f64 * cell_h + cell_h / 2.0 - frame_h / 2.0;
        dvec3(x, y, 0.0)
    };

    // Build all items as VItem, laid out in row-major order (4 cols x 4 rows)
    let star_points: Vec<DVec3> = (0..10)
        .map(|i| {
            let angle = i as f64 / 10.0 * PI * 2.0 + PI / 2.0;
            let rad = if i % 2 == 0 { 1.0 } else { 0.5 };
            dvec3(angle.cos() * rad, angle.sin() * rad, 0.0)
        })
        .chain(std::iter::once(dvec3(0.0, 1.0, 0.0)))
        .collect();

    let items: Vec<VItem> = vec![
        // Row 0: basic shapes
        Square::new(2.0)
            .with(|s| {
                s.set_color(manim::BLUE_C);
            })
            .into(),
        Rectangle::new(3.0, 1.5)
            .with(|s| {
                s.set_color(manim::RED_C);
            })
            .into(),
        Circle::new(1.0)
            .with(|c| {
                c.set_color(manim::GREEN_C);
            })
            .into(),
        Ellipse::new(dvec2(2.0, 1.0))
            .with(|e| {
                e.set_color(manim::TEAL_C);
            })
            .into(),
        // Row 1: arcs & regular polygon
        Arc::new(PI * 1.2, 1.0)
            .with(|a| {
                a.set_stroke_color(manim::YELLOW_D);
            })
            .into(),
        ArcBetweenPoints::new(dvec3(-1.0, -0.5, 0.0), dvec3(1.0, 0.5, 0.0), PI / 3.0)
            .with(|a| {
                a.set_stroke_color(manim::ORANGE);
            })
            .into(),
        EllipticArc::new(0.0, PI * 1.5, dvec2(1.5, 0.8))
            .with(|e| {
                e.set_stroke_color(manim::PINK);
            })
            .into(),
        Polygon::from(RegularPolygon::new(5, 1.0).with(|p| {
            p.set_color(manim::PURPLE_C);
        }))
        .into(),
        // Row 2: polygons & rotated shapes
        Polygon::new(vec![
            dvec3(0.0, 1.0, 0.0),
            dvec3(-1.0, -0.6, 0.0),
            dvec3(1.0, -0.6, 0.0),
            dvec3(0.0, 1.0, 0.0),
        ])
        .with(|p| {
            p.set_color(manim::MAROON_C);
        })
        .into(),
        Polygon::from(RegularPolygon::new(6, 1.0).with(|p| {
            p.set_color(manim::BLUE_D);
        }))
        .into(),
        Ellipse::new(dvec2(2.0, 0.8))
            .with(|e| {
                e.set_color(manim::RED_D);
                e.with_origin(AabbPoint::CENTER, |x| {
                    x.rotate_on_z(PI / 4.0);
                });
            })
            .into(),
        Polygon::new(star_points)
            .with(|p| {
                p.set_color(manim::YELLOW_B);
            })
            .into(),
        // Row 3: bezier curves — old (control-point min/max) vs new (exact extrema) AABB differ
        //
        // Single segment: handle at (0,3,0), curve only reaches y≈1.5
        VItem::from_vpoints(vec![
            dvec3(-1.0, 0.0, 0.0),
            dvec3(0.0, 3.0, 0.0),
            dvec3(1.0, 0.0, 0.0),
        ])
        .with(|v| {
            v.set_stroke_color(manim::GREEN_C);
        }),
        // S-curve: handles at y=±3, curve stays near y=0
        VItem::from_vpoints(vec![
            dvec3(-1.0, 0.0, 0.0),
            dvec3(-0.3, -3.0, 0.0),
            dvec3(0.0, 0.0, 0.0),
            dvec3(0.3, 3.0, 0.0),
            dvec3(1.0, 0.0, 0.0),
        ])
        .with(|v| {
            v.set_stroke_color(manim::RED_C);
        }),
        // Handle overshoots in x: handle at (3,2,0), endpoint at (1,0,0)
        VItem::from_vpoints(vec![
            dvec3(0.0, 0.0, 0.0),
            dvec3(3.0, 2.0, 0.0),
            dvec3(1.0, 0.0, 0.0),
        ])
        .with(|v| {
            v.set_stroke_color(manim::BLUE_C);
        }),
        // Symmetric loop: handles extend to x=±3
        VItem::from_vpoints(vec![
            dvec3(0.0, -0.5, 0.0),
            dvec3(-3.0, 2.0, 0.0),
            dvec3(0.0, 0.5, 0.0),
            dvec3(3.0, 2.0, 0.0),
            dvec3(0.0, -0.5, 0.0),
        ])
        .with(|v| {
            v.set_stroke_color(manim::YELLOW_D);
        }),
    ];

    for (i, item) in items.into_iter().enumerate() {
        let col = i % cols;
        let row = i / cols;
        let mut wrapped = VisualizeAabbItem(item).with(|v| {
            v.scale_to(ScaleHint::PorportionalY(target));
            v.move_to(cell_center(col, row));
        });
        let r_id = r.insert_empty();
        r.timeline_mut(r_id)
            .play(wrapped.show())
            .play(wrapped.rotating(PI * 2.0, DVec3::Z));
    }

    r.insert_time_mark(
        r.timelines().max_total_secs(),
        TimeMark::Capture("preview.png".to_string()),
    );
}
