//! Omitted Imports

enum AnimationType {
	Single,
	Directional,
	Unsupported,
}

pub fn build_animation_library<'a>(
	container: &mut Gd<AnimationLibrary>,
	anim_def: &'a AnimationData<'a>,
	atlas_def: &'a GeneratedAtlasSpec<'a>,
) {
	let frame_size = anim_def.frame_size;
	for (path, region) in &atlas_def.allocations {
		let animation_name = match file_name(path) {
			Some(name) => name,
			None => {
				godot_warn!("[Anim] Couldn't create name for animation at path: {}", path);
				continue;
			}
		};

		let frames = frame_count(frame_size, region);
		let (h_frame, v_frame) = first_frame(frame_size, region);
		let frame_time = anim_def
			.frame_time_override
			.get(animation_name.as_str())
			.copied()
			.unwrap_or(anim_def.default_frame_time);
		let should_loop = anim_def.should_loop.contains(animation_name.as_str());

		match infer_animation_type(frame_size, region) {
			AnimationType::Single => {
				let names = [format!("{}_right_down", &animation_name), animation_name];
				for name in names {
					let mut anim = animation(h_frame, v_frame, frames, frame_time);
					anim.set_loop_mode(if should_loop {
						LoopMode::LINEAR
					} else {
						LoopMode::NONE
					});

					container.add_animation(&StringName::from(name), &anim);
				}
			}
			AnimationType::Directional => {
				let names = ["right_down", "left_down", "right_up", "left_up"];
				for (v_diff, name) in names.iter().enumerate() {
					let mut anim = animation(h_frame, v_frame + v_diff as i32, frames, frame_time);

					anim.set_loop_mode(if should_loop {
						LoopMode::LINEAR
					} else {
						LoopMode::NONE
					});

					container
						.add_animation(&StringName::from(format!("{}_{}", &animation_name, name)), &anim);
				}
			}
			AnimationType::Unsupported => {
				godot_warn!("[Anim] Skipping unsupported animation layout for {}", path);
			}
		}
	}
}

#[inline(always)]
fn file_name(path: &str) -> Option<String> {
	PathBuf::from(path)
		.file_stem()
		.and_then(|stem| stem.to_str())
		.map(|stem| stem.to_snake_case())
}

#[inline(always)]
fn infer_animation_type(frame_size: i32, region: &Rectangle) -> AnimationType {
	if frame_size == region.height() {
		AnimationType::Single
	} else if frame_size * 4 == region.height() {
		AnimationType::Directional
	} else {
		AnimationType::Unsupported
	}
}

#[inline(always)]
fn frame_count(frame_size: i32, region: &Rectangle) -> i32 {
	region.width() / frame_size
}

#[inline(always)]
fn first_frame(frame_size: i32, region: &Rectangle) -> (i32, i32) {
	let top_left = region.min; // Pixel coordinates
	(top_left.x / frame_size, top_left.y / frame_size) // Convert to frame space
}

pub fn animation(start_h_frame: i32, const_v_frame: i32, length: i32, frame_duration: f32) -> Gd<Animation> {
	let mut anim = Animation::new_gd();
	anim.set_length(frame_duration * length as f32);

	let h_frame_track = anim.add_track(TrackType::VALUE);
	anim.track_set_path(h_frame_track, "BaseSprite:frame_coords");

	for frame_num in 0..length {
		let keyframe = frame_num as f64 * frame_duration as f64;
		let frame_value = Vector2i::new(start_h_frame + frame_num, const_v_frame);
		anim.track_insert_key(h_frame_track, keyframe, &frame_value.to_variant());
	}

	anim
}