use bevy::window::PrimaryWindow;

use crate::prelude::*;

pub(crate) struct UiPlugin;

impl Plugin for UiPlugin {
    fn build(&self, app: &mut App) {
        app.add_systems(Startup, initialize);
        app.add_systems(OnEnter(GameState::Menu), sync_display_mode);
        app.add_systems(
            Update,
            (
                toggle_display_mode.run_if(any_component_changed::<Interaction>),
                sync_display_mode
                    .run_if(state_changed::<DisplayState>())
                    .run_if(in_state(GameState::Menu)),
                sync_display_mode
                    .run_if(state_changed::<DisplayState>())
                    .run_if(in_state(GameState::Play)),
                manage_cursor.run_if(any_component_changed::<Interaction>),
                interactive_button.run_if(any_component_changed::<Interaction>),
            ),
        );
    }
}

#[derive(Debug, Component)]
struct UiRoot;

fn initialize(mut commands: Commands) {
    commands
        .spawn((
            NodeBundle {
                style: Style {
                    position_type: PositionType::Absolute,
                    right: Val::Px(5.0),
                    bottom: Val::Px(5.0),
                    ..default()
                },
                background_color: Color::WHITE.into(),
                visibility: Visibility::Hidden,
                ..default()
            },
            UiRoot,
        ))
        .with_children(|parent| {
            parent.spawn((
                ButtonBundle {
                    style: Style {
                        width: Val::Px(25.0),
                        height: Val::Px(25.0),
                        ..default()
                    },
                    background_color: Color::WHITE.into(),
                    ..default()
                },
                DisplayState::Display2d,
            ));
        });
}

/// When button is clicked, switch display state to that state and hide the other option.
/// Display2d -> Click -> Display2d mode & Display3d butto
fn toggle_display_mode(
    events: Query<(&Interaction, &DisplayState), (Changed<Interaction>, With<Button>)>,
    mut next_state: ResMut<NextState<DisplayState>>,
) {
    events
        .iter()
        .for_each(|(interaction, display_state)| match interaction {
            Interaction::Pressed => match display_state {
                DisplayState::Display2d => next_state.set(DisplayState::Display2d),
                DisplayState::Display3d => next_state.set(DisplayState::Display3d),
            },
            _ => (),
        });
}

/// Syncs the display button with the UI
/// Runs every time the DisplayState changes
fn sync_display_mode(
    mut query: Query<(&mut UiImage, &mut DisplayState), With<Button>>,
    mut visibilities: Query<&mut Visibility, With<UiRoot>>,
    state: Res<State<DisplayState>>,
    server: Res<AssetServer>,
) {
    query.iter_mut().for_each(|(mut image, mut display_state)| {
        let (texture, target_state) = match state.get() {
            DisplayState::Display2d => (server.load("images/3DIcon.png"), DisplayState::Display3d),
            DisplayState::Display3d => (server.load("images/2DIcon.png"), DisplayState::Display2d),
        };
        *image = UiImage {
            texture,
            ..default()
        };
        *display_state = target_state;
    });
    visibilities
        .iter_mut()
        .for_each(|mut visibility| *visibility = Visibility::Visible);
}

fn manage_cursor(
    mut window: Query<&mut Window, With<PrimaryWindow>>,
    state: Res<State<GameState>>,
    buttons: Query<&Interaction, With<Button>>,
) {
    window.single_mut().cursor.icon = {
        // In Loading state, icon is Progess/Wait
        if *state == GameState::Loading {
            CursorIcon::Wait
        // If a button is hovered, icon is hand
        } else if buttons.iter().any(|i| *i == Interaction::Pressed) {
            CursorIcon::Grabbing
        // If a button is clicked, icon is grab
        } else if buttons.iter().any(|i| *i == Interaction::Hovered) {
            CursorIcon::Hand
        // Default icon is Crosshair
        } else {
            CursorIcon::Crosshair
        }
    };
}

fn interactive_button(
    mut events: Query<(&mut BackgroundColor, &Interaction), (With<Button>, Changed<Interaction>)>,
    mut writer: EventWriter<audio::AudioEvent>,
) {
    events
        .iter_mut()
        .for_each(|(mut bg_color, interaction)| match interaction {
            Interaction::None => {
                bg_color.0.set_a(0.5);
            }
            Interaction::Hovered => {
                bg_color.0.set_a(0.75);
                writer.send(audio::AudioEvent::MenuHover);
            }
            Interaction::Pressed => {
                bg_color.0.set_a(1.0);
                writer.send(audio::AudioEvent::MenuSelect);
            }
        });
}
