use crate::{
    GameState,
    heads_database::{HeadDatabaseAsset, HeadsDatabase},
};
use bevy::{platform::collections::HashMap, prelude::*};
use bevy_asset_loader::prelude::*;

#[derive(AssetCollection, Resource)]
pub struct AudioAssets {
    #[asset(path = "sfx/music/02.ogg")]
    pub music: Handle<AudioSource>,
    #[asset(path = "sfx/ambient/downtown_loop.ogg")]
    pub ambient: Handle<AudioSource>,
    #[asset(path = "sfx/effects/key_collect.ogg")]
    pub key_collect: Handle<AudioSource>,
    #[asset(path = "sfx/effects/gate.ogg")]
    pub gate: Handle<AudioSource>,
    #[asset(path = "sfx/effects/cash_collect.ogg")]
    pub cash_collect: Handle<AudioSource>,
    #[asset(path = "sfx/ui/selection.ogg")]
    pub selection: Handle<AudioSource>,

    #[asset(path = "sfx/ui/invalid.ogg")]
    pub invalid: Handle<AudioSource>,
    #[asset(path = "sfx/ui/reloaded.ogg")]
    pub reloaded: Handle<AudioSource>,

    #[asset(path = "sfx/ui/cash_heal.ogg")]
    pub cash_heal: Handle<AudioSource>,

    #[asset(path = "sfx/abilities/throw.ogg")]
    pub throw: Handle<AudioSource>,
    #[asset(path = "sfx/abilities/throw-explosion.ogg")]
    pub throw_explosion: Handle<AudioSource>,
    #[asset(path = "sfx/abilities/jet.ogg")]
    pub jet: Handle<AudioSource>,
    #[asset(path = "sfx/abilities/gun.ogg")]
    pub gun: Handle<AudioSource>,
    #[asset(path = "sfx/abilities/crossbow.ogg")]
    pub crossbow: Handle<AudioSource>,
    #[asset(path = "sfx/abilities/heal.ogg")]
    pub healing: Handle<AudioSource>,
    #[asset(path = "sfx/abilities/missile-explosion.ogg")]
    pub missile_explosion: Handle<AudioSource>,

    #[asset(path = "sfx/ui/backpack_open.ogg")]
    pub backpack_open: Handle<AudioSource>,
    #[asset(path = "sfx/ui/backpack_close.ogg")]
    pub backpack_close: Handle<AudioSource>,

    #[asset(path = "sfx/effects/head_collect.ogg")]
    pub head_collect: Handle<AudioSource>,
    #[asset(path = "sfx/effects/secret_collected.ogg")]
    pub secret_head_collect: Handle<AudioSource>,
    #[asset(path = "sfx/effects/head_drop.ogg")]
    pub head_drop: Handle<AudioSource>,

    #[asset(path = "sfx/hit", collection(typed))]
    pub hit: Vec<Handle<AudioSource>>,
    #[asset(path = "sfx/heads", collection(mapped, typed))]
    pub head: HashMap<AssetFileName, Handle<AudioSource>>,
}

#[derive(AssetCollection, Resource)]
struct HeadsAssets {
    #[asset(path = "all.headsdb.ron")]
    heads: Handle<HeadDatabaseAsset>,
}

#[derive(AssetCollection, Resource)]
pub struct HeadDropAssets {
    #[asset(path = "models/head_drops", collection(mapped, typed))]
    pub meshes: HashMap<AssetFileName, Handle<Gltf>>,
}

#[derive(AssetCollection, Resource)]
pub struct UIAssets {
    #[asset(path = "font.ttf")]
    pub font: Handle<Font>,

    #[asset(path = "ui/head_bg.png")]
    pub head_bg: Handle<Image>,
    #[asset(path = "ui/head_regular.png")]
    pub head_regular: Handle<Image>,
    #[asset(path = "ui/head_damage.png")]
    pub head_damage: Handle<Image>,
    #[asset(path = "ui/selector.png")]
    pub head_selector: Handle<Image>,

    #[asset(path = "ui/camera.png")]
    pub camera: Handle<Image>,
}

#[derive(AssetCollection, Resource)]
pub struct GameAssets {
    #[asset(path = "textures/fx/impact.png")]
    pub impact_atlas: Handle<Image>,

    #[asset(path = "models/key.glb#Scene0")]
    pub mesh_key: Handle<Scene>,

    #[asset(path = "models/spawn.glb#Scene0")]
    pub mesh_spawn: Handle<Scene>,

    #[asset(path = "models/cash.glb#Scene0")]
    pub mesh_cash: Handle<Scene>,

    #[asset(path = "models/medic_particle.glb#Scene0")]
    pub mesh_heal_particle: Handle<Scene>,

    #[asset(path = "models/projectiles", collection(mapped, typed))]
    pub projectiles: HashMap<AssetFileName, Handle<Gltf>>,

    #[asset(path = "models/characters", collection(mapped, typed))]
    pub characters: HashMap<AssetFileName, Handle<Gltf>>,
}

pub struct LoadingPlugin;
impl Plugin for LoadingPlugin {
    fn build(&self, app: &mut App) {
        app.add_systems(OnExit(GameState::AssetLoading), on_exit);
        app.add_loading_state(
            LoadingState::new(GameState::AssetLoading)
                .continue_to_state(GameState::MapLoading)
                .load_collection::<AudioAssets>()
                .load_collection::<GameAssets>()
                .load_collection::<HeadsAssets>()
                .load_collection::<HeadDropAssets>()
                .load_collection::<UIAssets>(),
        );
    }
}

fn on_exit(
    mut cmds: Commands,
    res: Res<HeadsAssets>,
    mut assets: ResMut<Assets<HeadDatabaseAsset>>,
) {
    let asset = assets
        .remove(res.heads.id())
        .expect("headsdb failed to load");

    cmds.insert_resource(HeadsDatabase { heads: asset.0 });
}
