use bevy::prelude::*;

use crate::GameState;

#[derive(Component)]
pub struct Trail {
    points: Vec<Vec3>,
    col_start: LinearRgba,
    col_end: LinearRgba,
}

impl Trail {
    pub fn new(points: usize, col_start: LinearRgba, col_end: LinearRgba) -> Self {
        Self {
            points: Vec::with_capacity(points),
            col_start,
            col_end,
        }
    }

    pub fn with_pos(self, pos: Vec3) -> Self {
        let mut trail = self;
        trail.add(pos);
        trail
    }

    pub fn add(&mut self, pos: Vec3) {
        if self.points.len() >= self.points.capacity() {
            self.points.pop();
        }
        self.points.insert(0, pos);
    }
}

pub fn plugin(app: &mut App) {
    app.add_systems(
        FixedUpdate,
        update_trail.run_if(in_state(GameState::Playing)),
    );
}

fn update_trail(
    mut query: Query<(Entity, &mut Trail, &Gizmo, &GlobalTransform)>,
    global_transform: Query<&GlobalTransform>,
    mut gizmo_assets: ResMut<Assets<GizmoAsset>>,
) -> Result {
    for (e, mut trail, gizmo, pos) in query.iter_mut() {
        trail.add(pos.translation());

        let parent_transform = global_transform.get(e)?;

        let Some(gizmo) = gizmo_assets.get_mut(gizmo.handle.id()) else {
            continue;
        };

        gizmo.clear();

        let lerp_denom = trail.points.len() as f32;

        gizmo.linestrip_gradient(trail.points.iter().enumerate().map(|(i, pos)| {
            (
                GlobalTransform::from_translation(*pos)
                    .reparented_to(parent_transform)
                    .translation,
                trail.col_start.mix(&trail.col_end, i as f32 / lerp_denom),
            )
        }));
    }

    Ok(())
}
