///
/// TODO: Custom Asset: FmodEventMapper
///
use crate::prelude::*;
use bevy::prelude::*;
use bevy_fmod::prelude::AudioSource;
use bevy_fmod::prelude::*;

pub(crate) struct AudioPlugin;

impl Plugin for AudioPlugin {
    fn build(&self, app: &mut App) {
        app.add_event::<AudioEvent>();

        app.add_plugins(FmodPlugin {
            audio_banks_paths: &[
                "./assets/audio/Martian Chess Audio/Build/Desktop/Master.bank",
                "./assets/audio/Martian Chess Audio/Build/Desktop/Master.strings.bank",
                "./assets/audio/Martian Chess Audio/Build/Desktop/Music.bank",
                "./assets/audio/Martian Chess Audio/Build/Desktop/SFX.bank",
            ],
        });

        app.add_systems(OnEnter(GameState::Menu), play_background);
        app.add_systems(
            Update,
            (
                play_audio.run_if(any_component_added::<AudioSource>),
                audio_trigger.run_if(on_event::<AudioEvent>()),
                // play_background.run_if(on_event::<AssetEvent<AudioSettings>>()), TODO: Handle music hot-reload
            ),
        );
    }
}

#[derive(Event, Debug, PartialEq, Component, Clone)]
pub enum AudioEvent {
    MainMusic,
    StopMainMusic,
    MenuHover,
    MenuSelect,
    PickUp,
    PutDown,
    Idle,
    StopIdle,
    Invalid,
}

fn play_background(mut events: EventWriter<AudioEvent>) {
    events.send(AudioEvent::MainMusic);
}

fn play_audio(mut events: Query<&AudioSource, Added<AudioSource>>) {
    events.iter_mut().for_each(|aud| aud.play());
}

fn audio_trigger(
    mut events: EventReader<AudioEvent>,
    sources: Query<(&AudioSource, &AudioEvent)>,
    studio: Res<FmodStudio>,
    mut commands: Commands,
    server: Res<AssetServer>,
    tweaks: Res<Assets<tweak::Tweaks>>,
    display_state: Res<State<DisplayState>>,
) {
    let tweak = tweaks
        .get(&server.load("martian.tweak.toml"))
        .expect("Load tweaks");
    let state = display_state.get();
    events.read().for_each(|event| {
        let aud = match event {
            AudioEvent::MainMusic | AudioEvent::StopMainMusic => {
                tweak.get::<String>("audio_music_main").unwrap()
            }
            AudioEvent::MenuHover => tweak.get::<String>("audio_menu_hover").unwrap(),
            AudioEvent::MenuSelect => tweak.get::<String>("audio_menu_select").unwrap(),
            AudioEvent::PickUp => match state {
                DisplayState::Display2d => tweak.get::<String>("audio_display2d_pick_up").unwrap(),
                DisplayState::Display3d => tweak.get::<String>("audio_display3d_pick_up").unwrap(),
            },
            AudioEvent::PutDown => match state {
                DisplayState::Display2d => tweak.get::<String>("audio_display2d_put_down").unwrap(),
                DisplayState::Display3d => tweak.get::<String>("audio_display3d_put_down").unwrap(),
            },
            AudioEvent::Idle | AudioEvent::StopIdle => match state {
                DisplayState::Display2d => tweak.get::<String>("audio_display2d_idle").unwrap(),
                DisplayState::Display3d => tweak.get::<String>("audio_display3d_idle").unwrap(),
            },
            AudioEvent::Invalid => match state {
                DisplayState::Display2d => tweak.get::<String>("audio_display2d_invalid").unwrap(),
                DisplayState::Display3d => tweak.get::<String>("audio_display3d_invalid").unwrap(),
            },
        };
        // There is an event for this
        if !aud.is_empty() {
            let event_str = format!("event:{}", aud);
            if let Ok(event_description) = studio.0.get_event(event_str.as_str()) {
                let audio_source = AudioSource::new(event_description);
                // We are stopping a playing event
                match event {
                    // Find and stop playing instances of idle audio
                    AudioEvent::StopIdle => {
                        sources
                            .iter()
                            .filter(|(_, source_event)| **source_event == AudioEvent::Idle)
                            .for_each(|(source, _)| {
                                info!("Stopping audio {}", event_str);
                                source.stop();
                            });
                    }
                    // Find and stop playing instances of main music
                    AudioEvent::StopMainMusic => {
                        sources
                            .iter()
                            .filter(|(_, source_event)| **source_event == AudioEvent::MainMusic)
                            .for_each(|(source, _)| {
                                info!("Stopping audio {}", event_str);
                                source.stop();
                            });
                    }
                    // we are playing a sound
                    _ => {
                        info!("Playing audio {}", event_str);
                        commands.spawn((audio_source, event.clone()));
                    }
                }
            } else {
                warn!("No music set for {:?} in {:?}", event, state);
            }
        }
    });
}
