use super::*;

/// Systems run during loading state
pub struct LoadingPlugin;

impl Plugin for LoadingPlugin {
    fn build(&self, app: &mut App) {
        // PERF: We check all asset classes every frame, should be event based
        app.init_resource::<TrackLoadingProgress>()
            .init_state::<LoadingState>()
            .add_systems(PreUpdate, reset_progress)
            .add_systems(Startup, spawn_loading_screen)
            .add_systems(
                Update,
                (
                    (
                        track_loading::<AnimationClip>,
                        track_loading::<AudioSource>,
                        track_loading::<Font>,
                        track_loading::<Gltf>,
                        track_loading::<Image>,
                        track_loading::<Shader>,
                    )
                        .run_if(in_state(LoadingState::Active)),
                    toggle_state_visibility::<LoadingState>.run_if(state_changed::<LoadingState>),
                ),
            )
            .add_systems(PostUpdate, check_progress);
    }
}

#[derive(States, Clone, PartialEq, Eq, Hash, Debug, Default, Component)]
pub enum LoadingState {
    #[default]
    Active,
    Idle,
}

/// Resource for tracking asset loading progress
#[derive(Resource, Default)]
struct TrackLoadingProgress(Vec<bool>);

fn spawn_loading_screen(mut commands: Commands) {
    commands.spawn((
        Node {
            align_self: AlignSelf::Center,
            justify_self: JustifySelf::Center,
            ..default()
        },
        LoadingState::Active,
        children![(
            TextColor(WHITE.into()),
            Text::new("Credits"),
            BackgroundColor(BLACK.into())
        )],
    ));
}

/// At the start of the update clear progress
fn reset_progress(mut progress: ResMut<TrackLoadingProgress>) {
    progress.0.clear()
}

/// Track the progress of all assets in this asset class
fn track_loading<A: Asset>(
    assets: Res<Assets<A>>,
    server: Res<AssetServer>,
    mut progress: ResMut<TrackLoadingProgress>,
) {
    let all_loaded = assets
        .ids()
        .map(|id| server.load_state(id))
        .all(|load_state| !load_state.is_loading() && !load_state.is_failed());

    progress.0.push(all_loaded);
}

/// At the end of the frame check if all asset classes are loaded
fn check_progress(
    progress: Res<TrackLoadingProgress>,
    mut next_state: ResMut<NextState<LoadingState>>,
) {
    if progress.0.iter().all(|x| *x) {
        next_state.set(LoadingState::Idle);
    }
}
