#[cfg(feature = "ldtk_1_2_5")]
mod data_1_2_5;

#[cfg(feature = "ldtk_1_2_4")]
mod data_1_2_4;

use bevy::asset::{AssetLoader, BoxedFuture, LoadContext, LoadedAsset};
use bevy::reflect::{TypeUuid, Uuid};
#[cfg(feature = "ldtk_1_2_4")]
pub use data_1_2_4::*;
#[cfg(feature = "ldtk_1_2_5")]
pub use data_1_2_5::*;

#[derive(thiserror::Error)]
pub enum ParseError {
	#[error("Failed to parse file: {0}")]
	SerdeError(#[from] String),
}

impl TypeUuid for Project {
	const TYPE_UUID: Uuid = Uuid::from_u128(87988914102923589138720617793417023455);
}

impl Project {
	pub fn from_bytes(bytes: &[u8]) -> Result<Self, ParseError> {
		serde_json::from_slice(bytes).map_err(|e| ParseError::SerdeError(format!("{}", e)))
	}
}

pub type LdtkProject = Project;

impl<'a> TryFrom<&'a [u8]> for Project {
	type Error = ParseError;

	fn try_from(value: &'a [u8]) -> Result<Self, Self::Error> {
		Project::from_bytes(value)
	}
}

impl<'a> From<&'a [u8]> for Project {
	fn from(value: &'a [u8]) -> Self {
		#[cfg(feature = "no_panic")]
		{
			match Project::from_bytes(value) {
				Ok(val) => val,
				Err(e) => {
					log::error!("{}", e);
					std::process::abort();
				}
			}
		}

		#[cfg(not(feature = "no_panic"))]
		{
			Project::from_bytes(value).expect("Failed to parse ldtk project file")
		}
	}
}

#[derive(Default)]
pub struct LdtkLoader;
impl AssetLoader for LdtkLoader {
	fn load<'a>(
		&'a self,
		bytes: &'a [u8],
		load_context: &'a mut LoadContext,
	) -> BoxedFuture<'a, anyhow::Result<(), anyhow::Error>> {
		Box::pin(async move {
			load_context.set_default_asset(LoadedAsset::new(Project::from_bytes(bytes)?));
			Ok(())
		})
	}

	fn extensions(&self) -> &[&str] {
		&["ldtk"]
	}
}