//! `math_demo` — a runnable tour of the Stage 1 math primitives.
//!
//! Run with:
//! ```sh
//! cargo run -p oxide-examples --bin math_demo
//! ```
//!
//! This example has no window or GPU dependency; it simply exercises the math
//! API and prints results so the foundation can be reviewed by eye.

use oxide_engine::prelude::*;

fn main() {
    env_logger::init();
    log::info!("Oxide math demo — Stage 1 primitives");

    // --- Transform hierarchy (parent * child) ---
    let parent = Transform::from_trs(
        Vec3::new(10.0, 0.0, 0.0),
        Quat::from_rotation_y(90_f32.to_radians()),
        Vec3::splat(2.0),
    );
    let child_local = Transform::from_translation(Vec3::new(0.0, 0.0, 1.0));
    let child_world = parent.mul_transform(&child_local);

    println!("== Transform ==");
    println!("parent translation : {}", parent.translation);
    println!("child local pos    : {}", child_local.translation);
    println!("child world pos    : {}", child_world.translation);
    println!("parent forward     : {}", parent.forward());
    println!(
        "round-trip inverse : {}",
        parent.mul_transform(&parent.inverse()).translation
    );

    // --- Bounding box + ray pick ---
    let bounds = Aabb::from_points([
        Vec3::new(-1.0, -1.0, -1.0),
        Vec3::new(1.0, 2.0, 1.0),
        Vec3::new(0.5, 0.5, 3.0),
    ]);
    let ray = Ray::new(Vec3::new(0.0, 0.0, -10.0), Vec3::Z);
    println!("\n== AABB / Ray ==");
    println!("bounds center      : {}", bounds.center());
    println!("bounds size        : {}", bounds.size());
    match bounds.ray_intersection(&ray) {
        Some(t) => println!("ray hit at t={t:.3}, point={}", ray.at(t)),
        None => println!("ray missed the bounds"),
    }

    // --- Frustum culling ---
    let proj = Mat4::perspective_rh(60_f32.to_radians(), 16.0 / 9.0, 0.1, 100.0);
    let view = Mat4::look_at_rh(Vec3::new(0.0, 0.0, 5.0), Vec3::ZERO, Vec3::Y);
    let frustum = Frustum::from_view_projection(proj * view);
    let in_view = Aabb::from_center_half_extents(Vec3::ZERO, Vec3::splat(1.0));
    let off_screen = Aabb::from_center_half_extents(Vec3::new(0.0, 0.0, 100.0), Vec3::splat(1.0));
    println!("\n== Frustum ==");
    println!(
        "box at origin visible : {}",
        frustum.intersects_aabb(&in_view)
    );
    println!(
        "box behind camera     : {}",
        frustum.intersects_aabb(&off_screen)
    );

    // --- Color + value remap ---
    let sky = Color::from_hex(0x87CEEB);
    let ground = Color::from_hex(0x3A2E1F);
    let blended = sky.lerp(ground, 0.5);
    println!("\n== Color ==");
    println!("sky (linear)       : {:?}", sky.to_vec3());
    println!("blended sRGB bytes : {:?}", blended.to_srgb_u8());

    let value_range = Range3::new(Vec3::ZERO, Vec3::splat(100.0));
    println!("\n== Range3 ==");
    println!(
        "remap 25 → unit    : {}",
        value_range.inverse_lerp(Vec3::splat(25.0))
    );

    log::info!("math demo complete");
}
