#![feature(iter_array_chunks)] // used in ray.rs
#![feature(iter_intersperse)] // used in debug.rs
#![feature(async_closure)] // Loading tweakfiles

mod audio;
mod credits;
mod debug;
mod display3d;
mod game;
mod hit;
mod intro;
mod loading;
mod menu;
mod prelude;
mod tutorial;
mod tweak;
mod ui;

use std::time::Duration;

use crate::prelude::*;

fn main() {
    if std::env::var_os("CARGO_MANIFEST_DIR").is_none() {
        std::env::set_var("CARGO_MANIFEST_DIR", ".");
    }

    let mut app = App::new();
    app.add_state::<GameState>();
    app.add_state::<DisplayState>();
    app.add_systems(
        Update,
        (
            debug_state::<DisplayState>.run_if(resource_changed::<State<DisplayState>>()),
            debug_state::<GameState>.run_if(resource_changed::<State<GameState>>()),
            debug_state::<tutorial::TutorialState>
                .run_if(resource_changed::<State<tutorial::TutorialState>>()),
        ),
    );
    app.add_plugins(
        DefaultPlugins
            .set(ImagePlugin::default_nearest())
            .set(WindowPlugin {
                primary_window: Some(Window {
                    title: "Martian Chess".into(),
                    ..default()
                }),
                ..default()
            }),
    );

    app.add_plugins(credits::CreditsPlugin);
    app.add_plugins(debug::DebugPlugin);
    app.add_plugins(display3d::Display3dPlugin);
    app.add_plugins(game::GamePlugin);
    app.add_plugins(loading::LoadingPlugin);
    app.add_plugins(menu::MenuPlugin);
    app.add_plugins(audio::AudioPlugin);
    app.add_plugins(ui::UiPlugin);
    app.add_plugins(tweak::TweakPlugin);
    app.add_plugins(intro::IntroPlugin);
    app.add_plugins(tutorial::TutorialPlugin);
    app.run();
}

#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Default, States, Component)]
pub enum GameState {
    #[default]
    Loading,
    Menu,
    Credits,
    Intro,
    Play,
    Endgame,
}

#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Default, States, Component)]
pub(crate) enum DisplayState {
    #[default]
    Display3d,
}

/// System for printing the current state
///
/// Only runs when state is modified.
fn debug_state<S: States>(state: Res<State<S>>) {
    info!("State change {:?}", *state);
}

fn activate<Marker: Component>(
    mut entities: Query<
        &mut Visibility,
        (
            With<Marker>,
            Without<game::Captured>,
            Without<game::ValidMove>,
        ),
    >,
) {
    info!("Activating state");
    entities.iter_mut().for_each(|mut visibility| {
        *visibility = Visibility::Visible;
    });
}

fn deactivate<Marker: Component>(
    mut entities: Query<
        &mut Visibility,
        (With<Marker>, Without<game::Captured>, Without<GameState>),
    >,
) {
    entities.iter_mut().for_each(|mut visibility| {
        *visibility = Visibility::Hidden;
    });
}

pub(crate) fn any_component_changed<C: Component>(q: Query<Entity, Changed<C>>) -> bool {
    !q.is_empty()
}

pub(crate) fn any_component_added<C: Component>(q: Query<Entity, Added<C>>) -> bool {
    !q.is_empty()
}

pub(crate) fn _any_component_added_or_changed<C: Component>(
    q: Query<Entity, Or<(Added<C>, Changed<C>)>>,
) -> bool {
    !q.is_empty()
}

/// Stores the original value of a component which is modified at runtime
#[derive(Debug, Component)]
pub(crate) struct Original<T: Component>(T);
