#![allow(dead_code)]

use std::{collections::HashSet, f64::consts::PI, time::Duration};

use crate::engine::{Color, Key, Renderer, Scene, Shape, V3};

mod engine;

struct Game {
    objects: Vec<Object>,
    next_object_id: u32,
    keys_pressed: HashSet<Key>,
}

impl Game {
    fn new() -> Self {
        Self {
            objects: Vec::new(),
            next_object_id: 0,
            keys_pressed: HashSet::new(),
        }
    }

    fn spawn(&mut self, object_kind: ObjectKind) {
        let object = Object {
            kind: object_kind,
            id: self.next_object_id,
        };
        self.objects.push(object);
        self.next_object_id += 1
    }

    fn despawn(&mut self, id: u32) {
        let index = self
            .objects
            .iter()
            .position(|o| o.id == id)
            .expect("doesn't exist");
        self.objects.remove(index);
    }
}

impl<R: Renderer> engine::Game<R> for Game {
    fn update(&mut self, delta_time: Duration) {
        for object in &mut self.objects {
            if self.keys_pressed.contains(&Key::A) {
                match &mut object.kind {
                    ObjectKind::SkateBoard { pivot_deg, .. } => {
                        *pivot_deg -= 18.0 * delta_time.as_secs_f64();
                        if *pivot_deg < -12.5 {
                            *pivot_deg = -12.5;
                        }
                    }
                }
            }
            if self.keys_pressed.contains(&Key::D) {
                match &mut object.kind {
                    ObjectKind::SkateBoard {
                        pivot_deg: pivot_factor,
                        ..
                    } => {
                        *pivot_factor += 18.0 * delta_time.as_secs_f64();
                        if *pivot_factor > 12.5 {
                            *pivot_factor = 12.5;
                        }
                    }
                }
            }
            if !(self.keys_pressed.contains(&Key::A) || self.keys_pressed.contains(&Key::D)) {
                match &mut object.kind {
                    ObjectKind::SkateBoard {
                        pivot_deg: pivot_factor,
                        ..
                    } => {
                        let decay_rate = 1.0 - (2.0 * delta_time.as_secs_f64());
                        *pivot_factor *= decay_rate;
                    }
                }
            }
            if self.keys_pressed.contains(&Key::R) {
                match &mut object.kind {
                    ObjectKind::SkateBoard { rot, pos, .. } => {
                        *pos = V3(0.0, -0.1, -0.6);
                        *rot = V3(0.0, 0.5 * PI, 0.0);
                    }
                }
            }
            if self.keys_pressed.contains(&Key::W) {
                match &mut object.kind {
                    ObjectKind::SkateBoard { rot, .. } => rot.0 += 1.0 * delta_time.as_secs_f64(),
                }
            }
            if self.keys_pressed.contains(&Key::S) {
                match &mut object.kind {
                    ObjectKind::SkateBoard { rot, .. } => rot.0 -= 1.0 * delta_time.as_secs_f64(),
                }
            }
            if self.keys_pressed.contains(&Key::Left) {
                match &mut object.kind {
                    ObjectKind::SkateBoard { rot, .. } => rot.1 -= 1.0 * delta_time.as_secs_f64(),
                }
            }
            if self.keys_pressed.contains(&Key::Right) {
                match &mut object.kind {
                    ObjectKind::SkateBoard { rot, .. } => rot.1 += 1.0 * delta_time.as_secs_f64(),
                }
            }
            if self.keys_pressed.contains(&Key::Down) {
                match &mut object.kind {
                    ObjectKind::SkateBoard { pos, .. } => pos.1 -= 0.5 * delta_time.as_secs_f64(),
                }
            }
            if self.keys_pressed.contains(&Key::Up) {
                match &mut object.kind {
                    ObjectKind::SkateBoard { pos, .. } => pos.1 += 0.5 * delta_time.as_secs_f64(),
                }
            }
            object.update(delta_time);
        }
    }

    fn render(&mut self, r: &mut R) {
        let mut scene = Scene::new();
        for object in &self.objects {
            object.render(&mut scene);
        }
        scene.render(r, V3(0.0, 0.0, -1.0));
    }

    fn event(&mut self, event: engine::Event) {
        match event {
            engine::Event::KeyDown { key } => self.keys_pressed.insert(key),
            engine::Event::KeyUp { key } => self.keys_pressed.remove(&key),
        };
    }
}

struct Object {
    kind: ObjectKind,
    id: u32,
}

enum ObjectKind {
    SkateBoard {
        pos: V3,
        vel: V3,
        rot: V3,
        nyoom_factor: f64,
        pivot_deg: f64,
    },
}

struct ShapeGroupShape {
    shape: Shape,
    offset: V3,
}

struct ShapeGroup {
    pub shapes: Vec<ShapeGroupShape>,
}

impl ShapeGroup {
    pub fn new(shapes: Vec<ShapeGroupShape>) -> Self {
        Self { shapes }
    }
    pub fn rotate(mut self, rot: V3) -> Self {
        for shape in &mut self.shapes {
            shape.shape = shape
                .shape
                .translate(shape.offset)
                .rotate(rot)
                .translate(shape.offset * -1.0);
        }
        self
    }
    pub fn translate(mut self, offset: V3) -> Self {
        for shape in &mut self.shapes {
            shape.shape = shape.shape.translate(offset);
        }
        self
    }
    pub fn draw(self, pos: V3, scene: &mut Scene, outline_color: Color, fill_color: Color) {
        for shape in self.shapes {
            scene.draw_shape(
                pos,
                &shape.shape.translate(shape.offset),
                outline_color,
                fill_color,
            );
        }
    }
}

impl Object {
    fn update(&mut self, delta_time: Duration) {
        match &mut self.kind {
            ObjectKind::SkateBoard {
                pos,
                vel,
                nyoom_factor,
                pivot_deg: pivot_factor,
                ..
            } => {
                vel.0 = *pivot_factor * delta_time.as_secs_f64();
                *pos += *vel * delta_time.as_secs_f64();
                *nyoom_factor += 16.0 * delta_time.as_secs_f64();

                // rot.0 += delta_time.as_secs_f64() * PI * 1.0;
                // rot.1 += delta_time.as_secs_f64() * PI * 0.2;
                // rot.2 += delta_time.as_secs_f64() * PI * 2.0;
            }
        }
    }

    fn render(&self, scene: &mut Scene) {
        match self.kind {
            ObjectKind::SkateBoard {
                pos,
                rot,
                nyoom_factor,
                pivot_deg: pivot_factor,
                ..
            } => {
                let board_size = V3(0.175, 0.005, 0.05);
                let trucks = {
                    let anchor_size = V3(0.005, 0.01, 0.005);
                    let anchor_y = -anchor_size.1 - board_size.1 * 0.5;
                    let pivot = vec![
                        ShapeGroupShape {
                            shape: Shape::new_cube(anchor_size),
                            offset: V3(
                                anchor_size.0 * -0.5 + board_size.0 * 0.4,
                                anchor_y,
                                anchor_size.2 * -0.5,
                            ),
                        },
                        ShapeGroupShape {
                            shape: Shape::new_cube(anchor_size),
                            offset: V3(
                                anchor_size.0 * -0.5 - board_size.0 * 0.4,
                                anchor_y,
                                anchor_size.2 * -0.5,
                            ),
                        },
                    ];
                    let wheel_rail_size = V3(anchor_size.0, anchor_size.0, board_size.2 * 0.8);
                    let wheel_rail_y = anchor_y - wheel_rail_size.1;
                    let wheel_rail = vec![
                        ShapeGroupShape {
                            shape: Shape::new_cube(wheel_rail_size),
                            offset: V3(
                                wheel_rail_size.0 * -0.5 + board_size.0 * 0.4,
                                wheel_rail_y,
                                wheel_rail_size.2 * -0.5,
                            ),
                        },
                        ShapeGroupShape {
                            shape: Shape::new_cube(wheel_rail_size),
                            offset: V3(
                                wheel_rail_size.0 * -0.5 - board_size.0 * 0.4,
                                wheel_rail_y,
                                wheel_rail_size.2 * -0.5,
                            ),
                        },
                    ];
                    let wheel_size = V3(0.01, 0.01, 0.0025);
                    let wheel_y = wheel_rail_y - wheel_size.1 * 0.25;
                    let wheel_rot = V3(0.0, 0.0, nyoom_factor);
                    let wheel_rot_trans = V3(0.5, 0.5, 0.0);
                    let wheel = vec![
                        ShapeGroupShape {
                            shape: Shape::new_cube(wheel_size)
                                .translate(wheel_size * wheel_rot_trans * -1.0)
                                .rotate(wheel_rot)
                                .translate(wheel_size * wheel_rot_trans),
                            offset: V3(
                                wheel_size.0 * -0.5 + board_size.0 * 0.4,
                                wheel_y,
                                wheel_rail_size.2 * -0.5 - wheel_size.2,
                            ),
                        },
                        ShapeGroupShape {
                            shape: Shape::new_cube(wheel_size)
                                .translate(wheel_size * wheel_rot_trans * -1.0)
                                .rotate(wheel_rot)
                                .translate(wheel_size * wheel_rot_trans),

                            offset: V3(
                                wheel_size.0 * -0.5 - board_size.0 * 0.4,
                                wheel_y,
                                wheel_rail_size.2 * -0.5 - wheel_size.2,
                            ),
                        },
                        ShapeGroupShape {
                            shape: Shape::new_cube(wheel_size)
                                .translate(wheel_size * wheel_rot_trans * -1.0)
                                .rotate(wheel_rot)
                                .translate(wheel_size * wheel_rot_trans),

                            offset: V3(
                                wheel_size.0 * -0.5 + board_size.0 * 0.4,
                                wheel_y,
                                wheel_rail_size.2 * 0.5,
                            ),
                        },
                        ShapeGroupShape {
                            shape: Shape::new_cube(wheel_size)
                                .translate(wheel_size * wheel_rot_trans * -1.0)
                                .rotate(wheel_rot)
                                .translate(wheel_size * wheel_rot_trans),

                            offset: V3(
                                wheel_size.0 * -0.5 - board_size.0 * 0.4,
                                wheel_y,
                                wheel_rail_size.2 * 0.5,
                            ),
                        },
                    ];
                    vec![pivot, wheel_rail, wheel].into_iter().flatten()
                };
                let board_pivot_trans = V3(0.0, 0.5, 0.5);
                let mut board = vec![ShapeGroupShape {
                    shape: Shape::new_cube(board_size)
                        .translate(board_size * board_pivot_trans * -1.0)
                        .rotate(V3(pivot_factor * (PI / 180.0), 0.0, 0.0))
                        .translate(board_size * board_pivot_trans),
                    offset: board_size * -0.5,
                }];
                board.extend(trucks);
                let board = ShapeGroup::new(board).rotate(rot);
                board.draw(pos, scene, Color::Green, Color::Black);
                scene.draw_shape(
                    V3(-0.5, -5.0, 0.0),
                    &Shape::new_cube(V3(1.0, 0.0, 100.0)),
                    Color::Red,
                    Color::Red,
                );
            }
        }
    }
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut sdl_io = engine::SdlIo::new()?;
    let mut game = Game::new();
    let mut objects: Vec<ObjectKind> = Vec::new();
    objects.push(ObjectKind::SkateBoard {
        pos: V3(0.0, -0.1, -0.6),
        vel: V3(0.0, 0.0, 0.0),
        rot: V3(0.0, PI * 0.5, 0.0),
        nyoom_factor: 0.0,
        pivot_deg: 0.0,
    });

    for object in objects {
        game.spawn(object)
    }

    sdl_io.run(&mut game);
    Ok(())
}
