#![allow(clippy::too_many_arguments)]

#[cfg(not(target_arch = "wasm32"))]
mod audio;
#[cfg(not(target_arch = "wasm32"))]
mod editor;
mod filters;
mod game;
mod levels;
mod menu;
mod particle_effect;

use bevy::{
	asset::{Asset, HandleId, LoadState},
	prelude::*,
	window::{WindowId, WindowMode},
};
use bevy_common_assets::json::JsonAssetPlugin;
use bevy_rapier2d::prelude::*;

#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
enum AppState {
	Loading,
	Menu,
	Game,
	Win,
	Editor,
}

struct UseEditor(bool);

struct LoadingAssets(Vec<HandleId>);

impl LoadingAssets {
	fn add<T: Asset>(&mut self, handle: Handle<T>) -> Handle<T> {
		self.0.push(handle.id);
		handle
	}
}

fn main() {
	let (audio_event_sender, audio_event_receiver) =
		crossbeam_channel::bounded::<game::AudioMsg>(512);

	#[cfg(not(target_arch = "wasm32"))]
	std::thread::spawn(move || audio::setup(audio_event_receiver));

	#[cfg(not(target_arch = "wasm32"))]
	let (first_level, use_editor) = {
		let mut args = std::env::args().skip(1);
		(
			game::LevelId(args.next().map_or(0, |s| s.parse().unwrap_or(0))),
			args.next().map_or(false, |s| s == "e"),
		)
	};
	#[cfg(target_arch = "wasm32")]
	let (first_level, use_editor) = (game::LevelId(0), false);

	let mut app = App::new();
	app.insert_resource(Msaa { samples: 4 })
		.insert_resource(audio_event_sender)
		.insert_resource(UseEditor(use_editor))
		.add_state(AppState::Loading)
		.insert_resource(game::FirstLevel(first_level))
		.insert_resource(ClearColor(Color::BLACK))
		.add_plugins(DefaultPlugins)
		//.add_plugin(RapierDebugRenderPlugin::default())
		//.add_plugin(bevy_inspector_egui::WorldInspectorPlugin::new())
		.add_plugin(JsonAssetPlugin::<levels::StoredLevels>::new(&[
			"levels.json",
		]));

	if !use_editor {
		app.add_plugin(RapierPhysicsPlugin::<NoUserData>::pixels_per_meter(64.0))
			.add_plugin(menu::MenuPlugin)
			.add_plugin(game::GamePlugin)
			.add_plugin(particle_effect::ParticleEffectPlugin);
	}

	#[cfg(not(target_arch = "wasm32"))]
	if use_editor {
		app.add_plugin(editor::EditorPlugin);
	}

	app.add_system(keyboard_util_system)
		.add_startup_system(setup)
		.add_system_set(SystemSet::on_update(AppState::Loading).with_system(loading_system))
		.run();
}

fn setup(mut commands: Commands, mut windows: ResMut<Windows>, asset_server: Res<AssetServer>) {
	windows
		.get_mut(WindowId::primary())
		.unwrap()
		.set_title(String::from("Bevyjam"));

	let mut assets = LoadingAssets(Vec::new());
	commands.insert_resource(
		assets.add(asset_server.load::<levels::StoredLevels, _>("game.levels.json")),
	);
	commands.insert_resource(assets.add(asset_server.load::<Font, _>("UacariLegacy-Thin.ttf")));
	commands.insert_resource([
		assets.add(asset_server.load::<Image, _>("bevy.png")),
		assets.add(asset_server.load("melty.png")),
	]);
	commands.insert_resource(assets);

	commands.spawn_bundle(Camera2dBundle::default());
	commands.insert_resource(AmbientLight {
		color: Color::WHITE,
		brightness: 0.6,
	});
}

fn loading_system(
	asset_server: Res<AssetServer>,
	use_editor: Res<UseEditor>,
	assets: Res<LoadingAssets>,
	mut app_state: ResMut<State<AppState>>,
) {
	if asset_server.get_group_load_state(assets.0.iter().copied()) == LoadState::Loaded {
		app_state
			.replace(if use_editor.0 {
				AppState::Editor
			} else {
				AppState::Menu
			})
			.ok();
	}
}

fn keyboard_util_system(keyboard_input: Res<Input<KeyCode>>, mut windows: ResMut<Windows>) {
	#[cfg(not(target_arch = "wasm32"))]
	{
		if keyboard_input.just_released(KeyCode::Escape) {
			std::process::exit(0);
		}
		if keyboard_input.just_pressed(KeyCode::F11) {
			if let Some(window) = windows.get_primary_mut() {
				window.set_mode(match window.mode() {
					WindowMode::Windowed => WindowMode::Fullscreen,
					_ => WindowMode::Windowed,
				});
			}
		}
	}
}