use glam::Mat4;
use roast2d::{prelude::*, renderer::shader3d::draw3d::Draw3D};

fn vertex(pos: [i8; 3], tc: [i8; 2]) -> Mesh3DVertex {
    Mesh3DVertex {
        position: [pos[0] as f32, pos[1] as f32, pos[2] as f32],
        uv: [tc[0] as f32, tc[1] as f32],
    }
}

fn create_vertices() -> (Vec<Mesh3DVertex>, Vec<u16>) {
    let vertex_data = [
        // top (0, 0, 1)
        vertex([-1, -1, 1], [0, 0]),
        vertex([1, -1, 1], [1, 0]),
        vertex([1, 1, 1], [1, 1]),
        vertex([-1, 1, 1], [0, 1]),
        // bottom (0, 0, -1)
        vertex([-1, 1, -1], [1, 0]),
        vertex([1, 1, -1], [0, 0]),
        vertex([1, -1, -1], [0, 1]),
        vertex([-1, -1, -1], [1, 1]),
        // right (1, 0, 0)
        vertex([1, -1, -1], [0, 0]),
        vertex([1, 1, -1], [1, 0]),
        vertex([1, 1, 1], [1, 1]),
        vertex([1, -1, 1], [0, 1]),
        // left (-1, 0, 0)
        vertex([-1, -1, 1], [1, 0]),
        vertex([-1, 1, 1], [0, 0]),
        vertex([-1, 1, -1], [0, 1]),
        vertex([-1, -1, -1], [1, 1]),
        // front (0, 1, 0)
        vertex([1, 1, -1], [1, 0]),
        vertex([-1, 1, -1], [0, 0]),
        vertex([-1, 1, 1], [0, 1]),
        vertex([1, 1, 1], [1, 1]),
        // back (0, -1, 0)
        vertex([1, -1, 1], [0, 0]),
        vertex([-1, -1, 1], [1, 0]),
        vertex([-1, -1, -1], [1, 1]),
        vertex([1, -1, -1], [0, 1]),
    ];

    let index_data: &[u16] = &[
        0, 1, 2, 2, 3, 0, // top
        4, 5, 6, 6, 7, 4, // bottom
        8, 9, 10, 10, 11, 8, // right
        12, 13, 14, 14, 15, 12, // left
        16, 17, 18, 18, 19, 16, // front
        20, 21, 22, 22, 23, 20, // back
    ];

    (vertex_data.to_vec(), index_data.to_vec())
}

struct CubeMesh(Mesh3D);

struct Game {
    // Rotation controlled by mouse drag
    yaw: f32,
    pitch: f32,
    dragging: bool,
    last_mouse: Vec2,
    // Mesh
    cube_mesh: Option<CubeMesh>,
    // UI
    fps_text: Text,
    fps_timer: f32,
}

impl Default for Game {
    fn default() -> Self {
        Self {
            yaw: 0.0,
            pitch: 0.0,
            dragging: false,
            last_mouse: Vec2::ZERO,
            cube_mesh: None,
            fps_text: Text::new("FPS: --".into(), 14.0, YELLOW),
            fps_timer: 0.0,
        }
    }
}

impl Scene for Game {
    fn init(&mut self, g: &mut Engine) {
        // Build cube mesh once
        let (verts, indices) = create_vertices();
        self.cube_mesh
            .replace(CubeMesh(Mesh3D::new(g, &verts, &indices)));
    }

    fn update(&mut self, g: &mut Engine) {
        // Mouse drag to rotate
        let input = g.input();
        if input.key_just_pressed(KeyCode::MouseLeft) {
            self.dragging = true;
            self.last_mouse = input.mouse_pos();
        }
        if input.key_just_released(KeyCode::MouseLeft) {
            self.dragging = false;
        }
        if self.dragging && input.key_down(KeyCode::MouseLeft) {
            let curr = input.mouse_pos();
            let delta = curr - self.last_mouse;
            self.last_mouse = curr;
            // Sensitivity tuned for logical units
            let sens = 0.01;
            self.yaw += delta.x * sens;
            self.pitch += delta.y * sens;
            self.pitch = self.pitch.clamp(-1.4, 1.4);
        }

        // Update FPS text a few times per second to avoid churn
        self.fps_timer += g.tick;
        if self.fps_timer > 0.25 {
            self.fps_timer = 0.0;
            let fps = if g.tick > 0.0 { 1.0 / g.tick } else { 0.0 };
            self.fps_text = Text::new(format!("FPS: {:.0}", fps), 14.0, YELLOW);
        }
    }

    fn draw(&mut self, g: &mut Engine) {
        // Set camera and queue 3D draw; 3D renders before 2D each frame.
        let cam = Camera3D {
            eye: Vec3::new(1.5, -5.0, 3.0),
            target: Vec3::ZERO,
            up: Vec3::Y,
            fov_y_radians: std::f32::consts::FRAC_PI_4,
            znear: 0.1,
            zfar: 10.0,
        };
        *g.camera3d_mut() = cam;
        let model = Mat4::from_rotation_y(self.yaw) * Mat4::from_rotation_x(self.pitch);
        g.draw3d(Draw3D::new(
            self.cube_mesh.as_ref().unwrap().0.clone(),
            model,
        ));

        // UI: FPS top-right
        g.with_ui(|g| {
            let view = g.view_size();
            let half = view * 0.5;
            g.draw_text(
                &self.fps_text,
                Some(Vec2::new(1.0, 0.0)),
                Transform::new(Vec3::new(half.x - 8.0, half.y - 16.0, 0.95)),
            );
        });
    }
}

fn main() {
    App::default().vsync(true).run(setup).unwrap();
}

fn setup(g: &mut Engine) {
    // set resize and scale
    g.set_scale_mode(ScaleMode::Fixed {
        width: 800,
        height: 600,
    });

    g.set_scene(Game::default());
}
