use crate::track::Track;
use bevy::{
    math::{cubic_splines::CubicCurve, vec3},
    prelude::*,
    render::{
        mesh::{Indices, PrimitiveTopology},
        render_asset::RenderAssetUsages,
        texture::{ImageAddressMode, ImageLoaderSettings, ImageSampler, ImageSamplerDescriptor},
    },
};
use std::f32::consts::PI;

use rand::{thread_rng, Rng};

#[derive(Debug, Clone, Copy)]
enum Cube {
    Rock,
    Ring { position: Vec3, look_at: Vec3 },
    Empty,
}

pub fn build_level(
    mut commands: Commands,
    assets: ResMut<AssetServer>,
    mut meshes: ResMut<Assets<Mesh>>,
    mut images: ResMut<Assets<Image>>,
    mut mat: ResMut<Assets<StandardMaterial>>,
) {
    let mut model: [[[Cube; 10]; 10]; 10] = [[[Cube::Empty; 10]; 10]; 10];
    let mut rocks_ct = 300;
    let rock = assets.load("Rock 1.glb#Scene0");
    let ring = assets.load("Ring.glb#Scene0");

    let mut r = thread_rng();

    while rocks_ct > 0 {
        let (x, y, z) = (r.gen_range(0..10), r.gen_range(0..10), r.gen_range(0..10));
        match model[x][y][z] {
            Cube::Empty => {
                rocks_ct -= 1;
                model[x][y][z] = Cube::Rock;
            }
            _ => (),
        }
    }
    let mut curve_points = vec![Vec3::new(0.0, 0.0, -10.0), Vec3::new(0.0, 0.0, -20.0)];
    for _ in 0..8 {
        let point = vec3(
            (-0.5 + r.gen::<f32>()) * 200.0,
            (-0.5 + r.gen::<f32>()) * 200.0,
            (-0.5 + r.gen::<f32>()) * 200.0,
        );
        curve_points.push(point);
    }
    // Push all the curve points away from each other so the course feels more open.

    for round in 0..10 {
        for i in 0..curve_points.len() {
            let mut p = curve_points[i].clone();
            for j in 0..curve_points.len() {
                if i != j {
                    let other = curve_points[j].clone();
                    let dist = (p - other).length();
                    p -= (other - curve_points[i]) * 10.0 / (dist * dist);
                }
            }
            curve_points[i] = p;
        }
    }
    // swap the order of some curve poitnts to make the course less loopy.
    for i in 0..curve_points.len() {
        for j in 1..curve_points.len() - 2 {
            let v1 = (curve_points[j + 1] - curve_points[j]).normalize();
            let v2 = (curve_points[j + 2] - curve_points[j + 1]).normalize();
            if v1.dot(v2) < 0.75 {
                let temp = curve_points[j + 1];
                curve_points[j + 1] = curve_points[j + 2];
                curve_points[j + 2] = temp;
            }
        }
    }
    let track = Track::new(CubicCardinalSpline::new(0.5, curve_points).to_curve(), 1.0, 16, 200);
    for t in 0..10 {
        let t = t as f32;
        let p2 = track.curve.position(t);
        model[((p2.x / 20.0).round() + 4.5) as usize][((p2.y / 20.0).round() + 4.5) as usize]
            [((p2.z / 20.0).round() + 4.5) as usize] = Cube::Ring {
            position: p2.clone(),
            look_at: track.curve.velocity(t).normalize(),
        };
    }

    for i in 0..10 {
        for j in 0..10 {
            for k in 0..10 {
                match model[i][j][k] {
                    Cube::Rock => {
                        commands.spawn(SceneBundle {
                            scene: rock.clone(),
                            transform: Transform::from_xyz(
                                (-4.5 + i as f32) * 20.0,
                                (-4.5 + j as f32) * 20.0,
                                (-4.5 + k as f32) * 20.0,
                            ), //.with_scale(r.gen::<f32>() * Vec3::splat(3.0)),
                            ..default()
                        });
                    }
                    Cube::Ring { position, look_at } => {
                        commands.spawn(SceneBundle {
                            scene: ring.clone(),
                            transform: Transform::from_xyz(position.x, position.y, position.z)
                                .looking_to(look_at, Vec3::ZERO)
                                .with_scale(Vec3::splat(3.0)),

                            ..default()
                        });
                    }
                    Cube::Empty => (),
                };
            }
        }
    }

    // set up track.
    let texture_handle = assets.load_with_settings("checkerboard_albedo.png", |s: &mut _| {
        *s = ImageLoaderSettings {
            sampler: ImageSampler::Descriptor(ImageSamplerDescriptor {
                // rewriting mode to repeat image,
                address_mode_u: ImageAddressMode::Repeat,
                address_mode_v: ImageAddressMode::Repeat,
                ..default()
            }),
            ..default()
        }
    });

    let mesh = track.mesh();
    let mut texture_mat = StandardMaterial::default();
    texture_mat.base_color_texture = Some(texture_handle.clone());
    commands.spawn(PbrBundle {
        mesh: meshes.add(mesh),
        material: mat.add(texture_mat),
        ..default()
    });
}