use std::f32::consts::TAU;

use bevy::prelude::*;
use rand::prelude::*;

use crate::planet::PLANET_RADIUS;

pub const LIGHT_RANGE: f32 = 90.0;

#[derive(Component)]
struct AnimatedCyberLight {
    axis: Vec3,
    rate: f32,
}

#[derive(Component, Default)]
pub(crate) struct AnimateCyberLightWireframe {
    pub wired: bool,
}

fn spawn_moving_lights(
    mut commands: Commands,
    mut meshes: ResMut<Assets<Mesh>>,
    mut materials: ResMut<Assets<StandardMaterial>>,
) {
    let rng = &mut thread_rng();
    // spawn orbiting bisexual lights
    for _ in 0..655 {
        // mechanics
        let axis = crate::random_unit_vec(rng);
        let angle = rng.gen_range(0.0..TAU);
        let rate: f32 = rng.gen_range(7.0..10.0);
        let rate = rate.to_radians();
        let rotation = Quat::from_axis_angle(axis, angle);
        let altitude = PLANET_RADIUS + rng.gen_range(8.0..20.0);
        let perp = axis.any_orthonormal_vector();
        let translation = perp * altitude;
        let transform = Transform::from_translation(translation);

        // optics
        let hue = rng.gen_range(240.0..300.0);
        let saturation = rng.gen_range(0.85..0.99);
        let lightness = rng.gen_range(0.3..0.7);
        let color = Color::hsl(hue, saturation, lightness);
        let intensity = rng.gen_range(900.0..1300.0);
        let radius = rng.gen::<f32>() * 2.2; // why can't this infer the gen type?
        let point_light = PointLight {
            intensity,
            range: LIGHT_RANGE,
            color,
            radius: radius - 0.1,
            shadows_enabled: true,
            ..Default::default()
        };

        let sbundle = SpatialBundle {
            transform: Transform::from_rotation(rotation),
            ..Default::default()
        };
        commands
            // first, spawn an entity with a transform we can rotate
            .spawn((AnimatedCyberLight { axis, rate },))
            .insert(sbundle)
            .with_children(|parent| {
                parent
                    // now spawn a child entity with a pointlight, and a relative transform that's
                    // just translation from the parent
                    .spawn(PointLightBundle {
                        transform,
                        point_light,
                        ..Default::default()
                    })
                    .with_children(|builder| {
                        builder
                            // now a simple mesh to show a wireframe.
                            .spawn(PbrBundle {
                                mesh: meshes.add(Mesh::from(shape::Icosphere {
                                    radius,
                                    subdivisions: 1,
                                })),
                                material: materials.add(StandardMaterial {
                                    base_color: Color::hsla(272.0, 0.7, 0.56, 0.7),
                                    emissive: color,
                                    ..Default::default()
                                }),
                                ..Default::default()
                            })
                            .insert(AnimateCyberLightWireframe::default());
                    }); // mesh child
            }); // light child
    }
}

fn spawn_static_lights(
    mut commands: Commands,
    mut meshes: ResMut<Assets<Mesh>>,
    mut materials: ResMut<Assets<StandardMaterial>>,
) {
    let pink_light = PointLight {
        intensity: 1_00.0,
        range: LIGHT_RANGE,
        color: Color::PINK,
        radius: 1.0,
        shadows_enabled: true,
        ..Default::default()
    };

    let blue_light = PointLight {
        intensity: 1_000.0,
        range: LIGHT_RANGE,
        color: Color::BLUE,
        radius: 1.0,
        shadows_enabled: true,
        ..Default::default()
    };

    commands.insert_resource(AmbientLight {
        color: Color::WHITE,
        brightness: 0.2,
    });

    // up light
    commands
        .spawn(PointLightBundle {
            transform: Transform::from_xyz(0.0, PLANET_RADIUS + 30.0, 0.0),
            point_light: pink_light,
            ..Default::default()
        })
        .with_children(|builder| {
            builder.spawn(PbrBundle {
                mesh: meshes.add(Mesh::from(shape::Icosphere {
                    radius: 10.0,
                    subdivisions: 2,
                })),
                material: materials.add(StandardMaterial {
                    base_color: Color::BLUE,
                    emissive: Color::PINK,
                    ..Default::default()
                }),
                ..Default::default()
            });
        });
    // down light
    commands
        .spawn(PointLightBundle {
            transform: Transform::from_xyz(0.0, -PLANET_RADIUS - 30.0, 0.0),
            point_light: blue_light,
            ..Default::default()
        })
        .with_children(|builder| {
            builder.spawn(PbrBundle {
                mesh: meshes.add(Mesh::from(shape::Icosphere {
                    radius: 10.0,
                    subdivisions: 2,
                })),
                material: materials.add(StandardMaterial {
                    base_color: Color::PINK,
                    emissive: Color::BLUE,
                    ..Default::default()
                }),
                ..Default::default()
            });
        });
}

fn orbit_lights(time: Res<Time>, mut query: Query<(&mut Transform, &AnimatedCyberLight)>) {
    let dt = time.delta_seconds();
    for (mut transform, light) in query.iter_mut() {
        let AnimatedCyberLight { axis, rate } = *light;
        let theta = rate * dt;
        transform.rotation *= Quat::from_axis_angle(axis, theta);
    }
}

pub struct CyberSpaceLightsPlugin;
impl Plugin for CyberSpaceLightsPlugin {
    fn build(&self, app: &mut App) {
        app.add_startup_system(spawn_static_lights)
            .add_startup_system(spawn_moving_lights)
            .add_system(orbit_lights);
    }
}
