use bevy::prelude::*;
use de_core::{
    cleanup::DespawnOnGameExit, gamestate::GameState, gconfig::GameConfig,
    objects::ObjectTypeComponent, schedule::InputSchedule, state::AppState,
};
use de_spawner::{DraftAllowed, DraftBundle, SpawnLocalActiveEvent};
use de_types::objects::{BuildingType, ObjectType};
use crate::mouse::{Pointer, PointerSet};
pub(crate) struct DraftPlugin;
impl Plugin for DraftPlugin {
    fn build(&self, app: &mut App) {
        app.add_event::<SpawnDraftsEvent>()
            .add_event::<NewDraftEvent>()
            .add_event::<DiscardDraftsEvent>()
            .add_systems(
                InputSchedule,
                (
                    (
                        spawn
                            .run_if(on_event::<SpawnDraftsEvent>())
                            .in_set(DraftSet::Spawn),
                        new_drafts.in_set(DraftSet::New),
                        discard_drafts
                            .run_if(on_event::<DiscardDraftsEvent>())
                            .in_set(DraftSet::Discard),
                    )
                        .run_if(in_state(AppState::InGame)),
                    move_drafts
                        .run_if(in_state(GameState::Playing))
                        .after(PointerSet::Update),
                ),
            );
    }
}
#[derive(Copy, Clone, Hash, Debug, PartialEq, Eq, SystemSet)]
pub(crate) enum DraftSet {
    Spawn,
    New,
    Discard,
}
#[derive(Event)]
pub(crate) struct SpawnDraftsEvent;
#[derive(Event)]
pub(crate) struct NewDraftEvent {
    point: Vec3,
    building_type: BuildingType,
}
#[derive(Event)]
pub(crate) struct DiscardDraftsEvent;
impl NewDraftEvent {
    pub(crate) fn new(point: Vec3, building_type: BuildingType) -> Self {
        Self {
            point,
            building_type,
        }
    }
    fn point(&self) -> Vec3 {
        self.point
    }
    fn building_type(&self) -> BuildingType {
        self.building_type
    }
}
fn spawn(
    mut commands: Commands,
    game_config: Res<GameConfig>,
    drafts: Query<(Entity, &Transform, &ObjectTypeComponent, &DraftAllowed)>,
    mut spawn_active_events: EventWriter<SpawnLocalActiveEvent>,
) {
    for (entity, &transform, &object_type, draft) in drafts.iter() {
        if draft.allowed() {
            commands.entity(entity).despawn_recursive();
            let ObjectType::Active(object_type) = *object_type else {
                panic!("Cannot place draft of an inactive object.");
            };
            spawn_active_events.send(SpawnLocalActiveEvent::stationary(
                object_type,
                transform,
                game_config.locals().playable(),
            ));
        }
    }
}
fn new_drafts(
    mut commands: Commands,
    mut events: EventReader<NewDraftEvent>,
    drafts: Query<Entity, With<DraftAllowed>>,
) {
    let event = match events.read().last() {
        Some(event) => event,
        None => return,
    };
    for entity in drafts.iter() {
        commands.entity(entity).despawn_recursive();
    }
    commands.spawn((
        DraftBundle::new(
            event.building_type(),
            Transform {
                translation: event.point(),
                ..Default::default()
            },
        ),
        DespawnOnGameExit,
    ));
}
fn discard_drafts(mut commands: Commands, drafts: Query<Entity, With<DraftAllowed>>) {
    for entity in drafts.iter() {
        commands.entity(entity).despawn_recursive();
    }
}
fn move_drafts(pointer: Res<Pointer>, mut drafts: Query<&mut Transform, With<DraftAllowed>>) {
    let pointer_changed = pointer.is_changed();
    let point = match pointer.terrain_point() {
        Some(point) => point,
        None => return,
    };
    for mut transform in drafts.iter_mut() {
        if transform.is_added() || pointer_changed {
            transform.translation = point;
        }
    }
}