use ahash::AHashSet;
use bevy::{ecs::system::SystemParam, prelude::*};
use de_core::{gamestate::GameState, schedule::InputSchedule};
use de_signs::{UpdateBarVisibilityEvent, UpdateLineVisibilityEvent, UpdatePoleVisibilityEvent};
use de_terrain::MarkerVisibility;
use crate::SELECTION_BAR_ID;
pub(super) struct BookkeepingPlugin;
impl Plugin for BookkeepingPlugin {
    fn build(&self, app: &mut App) {
        app.add_event::<SelectEvent>()
            .add_event::<SelectedEvent>()
            .add_event::<DeselectedEvent>()
            .add_systems(
                InputSchedule,
                (
                    update_selection.in_set(SelectionSet::Update),
                    (selected_system, deselected_system).after(SelectionSet::Update),
                )
                    .run_if(in_state(GameState::Playing)),
            );
    }
}
#[derive(Copy, Clone, Hash, Debug, PartialEq, Eq, SystemSet)]
pub(crate) enum SelectionSet {
    Update,
}
#[derive(Event)]
pub(crate) struct SelectEvent {
    entities: Vec<Entity>,
    mode: SelectionMode,
}
impl SelectEvent {
    pub(crate) fn none(mode: SelectionMode) -> Self {
        Self {
            entities: Vec::new(),
            mode,
        }
    }
    pub(crate) fn single(entity: Entity, mode: SelectionMode) -> Self {
        Self {
            entities: vec![entity],
            mode,
        }
    }
    pub(crate) fn many(entities: Vec<Entity>, mode: SelectionMode) -> Self {
        Self { entities, mode }
    }
    fn entities(&self) -> &[Entity] {
        self.entities.as_slice()
    }
    fn mode(&self) -> SelectionMode {
        self.mode
    }
}
#[derive(Event)]
struct SelectedEvent(Entity);
#[derive(Event)]
struct DeselectedEvent(Entity);
#[derive(Component)]
pub(crate) struct Selected;
#[derive(Clone, Copy, PartialEq)]
pub(crate) enum SelectionMode {
    Replace,
    Add,
    AddToggle,
}
#[derive(SystemParam)]
struct SelectorBuilder<'w, 's> {
    commands: Commands<'w, 's>,
    selected: Query<'w, 's, Entity, With<Selected>>,
    selected_events: EventWriter<'w, SelectedEvent>,
    deselected_events: EventWriter<'w, DeselectedEvent>,
}
impl<'w, 's> SelectorBuilder<'w, 's> {
    fn build(self) -> Selector<'w, 's> {
        let selected: AHashSet<Entity> = self.selected.iter().collect();
        Selector {
            commands: self.commands,
            selected,
            to_select: AHashSet::new(),
            to_deselect: AHashSet::new(),
            selected_events: self.selected_events,
            deselected_events: self.deselected_events,
        }
    }
}
struct Selector<'w, 's> {
    commands: Commands<'w, 's>,
    selected: AHashSet<Entity>,
    to_select: AHashSet<Entity>,
    to_deselect: AHashSet<Entity>,
    selected_events: EventWriter<'w, SelectedEvent>,
    deselected_events: EventWriter<'w, DeselectedEvent>,
}
impl<'w, 's> Selector<'w, 's> {
    fn update(&mut self, entities: &[Entity], mode: SelectionMode) {
        let updated: AHashSet<Entity> = entities.iter().cloned().collect();
        match mode {
            SelectionMode::Replace => {
                self.to_select = &updated - &self.selected;
                self.to_deselect = &self.selected - &updated;
            }
            SelectionMode::AddToggle => {
                self.to_select = &(&updated - &self.to_select) - &self.selected;
                self.to_deselect = &updated & &self.selected;
            }
            SelectionMode::Add => {
                self.to_select.extend(&updated - &self.selected);
                self.to_deselect = &self.to_deselect - &updated;
            }
        }
    }
    fn execute(mut self) {
        for entity in self.to_deselect {
            self.commands.entity(entity).remove::<Selected>();
            self.deselected_events.send(DeselectedEvent(entity));
        }
        for entity in self.to_select {
            self.commands.entity(entity).insert(Selected);
            self.selected_events.send(SelectedEvent(entity));
        }
    }
}
fn update_selection(mut events: EventReader<SelectEvent>, selector_builder: SelectorBuilder) {
    let mut selector = selector_builder.build();
    for event in events.read() {
        selector.update(event.entities(), event.mode());
    }
    selector.execute();
}
fn selected_system(
    mut events: EventReader<SelectedEvent>,
    mut markers: Query<&mut MarkerVisibility>,
    mut bars: EventWriter<UpdateBarVisibilityEvent>,
    mut poles: EventWriter<UpdatePoleVisibilityEvent>,
    mut lines: EventWriter<UpdateLineVisibilityEvent>,
) {
    for event in events.read() {
        if let Ok(mut visibility) = markers.get_mut(event.0) {
            visibility.0.update_visible(SELECTION_BAR_ID, true);
        }
        bars.send(UpdateBarVisibilityEvent::new(
            event.0,
            SELECTION_BAR_ID,
            true,
        ));
        poles.send(UpdatePoleVisibilityEvent::new(event.0, true));
        lines.send(UpdateLineVisibilityEvent::new(event.0, true));
    }
}
fn deselected_system(
    mut events: EventReader<DeselectedEvent>,
    mut markers: Query<&mut MarkerVisibility>,
    mut bars: EventWriter<UpdateBarVisibilityEvent>,
    mut poles: EventWriter<UpdatePoleVisibilityEvent>,
    mut lines: EventWriter<UpdateLineVisibilityEvent>,
) {
    for event in events.read() {
        if let Ok(mut visibility) = markers.get_mut(event.0) {
            visibility.0.update_visible(SELECTION_BAR_ID, false);
        }
        bars.send(UpdateBarVisibilityEvent::new(
            event.0,
            SELECTION_BAR_ID,
            false,
        ));
        poles.send(UpdatePoleVisibilityEvent::new(event.0, false));
        lines.send(UpdateLineVisibilityEvent::new(event.0, false));
    }
}