use bevy::prelude::*;
use lightyear::prelude::input::native::ActionState;
use shared::{
    GameState,
    backpack::{
        BackbackSwapEvent, Backpack, UiHeadState,
        backpack_ui::{BackpackUiState, HEAD_SLOTS},
    },
    control::ControlState,
    protocol::PlaySound,
};

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

fn swap_head_inputs(
    player: Query<(&ActionState<ControlState>, Ref<Backpack>)>,
    mut commands: Commands,
    mut state: Single<&mut BackpackUiState>,
    time: Res<Time>,
) {
    for (controls, backpack) in player.iter() {
        if state.count == 0 {
            return;
        }

        if controls.backpack_toggle {
            state.open = !state.open;
            commands.trigger(PlaySound::Backpack { open: state.open });
        }

        if !state.open {
            return;
        }

        let mut changed = false;
        if controls.backpack_left && state.current_slot > 0 {
            state.current_slot -= 1;
            changed = true;
        }
        if controls.backpack_right && state.current_slot < state.count.saturating_sub(1) {
            state.current_slot += 1;
            changed = true;
        }
        if controls.backpack_swap {
            commands.trigger(BackbackSwapEvent(state.current_slot));
        }

        if changed {
            commands.trigger(PlaySound::Selection);
            sync(&backpack, &mut state, time.elapsed_secs());
        }
    }
}

fn sync_on_change(
    backpack: Query<Ref<Backpack>>,
    mut state: Single<&mut BackpackUiState>,
    time: Res<Time>,
) {
    for backpack in backpack.iter() {
        if backpack.is_changed() || backpack.reloading() {
            sync(&backpack, &mut state, time.elapsed_secs());
        }
    }
}

fn sync(backpack: &Backpack, state: &mut Single<&mut BackpackUiState>, time: f32) {
    state.count = backpack.heads.len();

    state.scroll = state.scroll.min(state.count.saturating_sub(HEAD_SLOTS));

    if state.current_slot >= state.scroll + HEAD_SLOTS {
        state.scroll = state.current_slot.saturating_sub(HEAD_SLOTS - 1);
    }
    if state.current_slot < state.scroll {
        state.scroll = state.current_slot;
    }

    for i in 0..HEAD_SLOTS {
        if let Some(head) = backpack.heads.get(i + state.scroll) {
            state.heads[i] = Some(UiHeadState::new(*head, time));
        } else {
            state.heads[i] = None;
        }
    }
}
