extends Node3D
class_name LilguyBody

const LERP_VELOCITY: float = 0.15

@export_category("Objects")
@export var _character: CharacterBody3D = null
@export var animation_player: AnimationPlayer = null

func apply_rotation(_velocity: Vector3) -> void:
	var new_rotation_y = lerp_angle(rotation.y, atan2(_velocity.x, _velocity.z), LERP_VELOCITY)
	rotation.y = new_rotation_y

## Instantly face a direction (no lerp) - used to lock attacks to camera facing
func snap_rotation(_direction: Vector3) -> void:
	rotation.y = atan2(_direction.x, _direction.z)

func animate(_velocity: Vector3) -> void:
	if not animation_player:
		return

	# Don't override attack animation if it's playing
	if animation_player.is_playing() and animation_player.current_animation.begins_with("Attack"):
		return

	# Check if we're dashing (defensive check for enemies that don't have this)
	if _character and "_is_dashing" in _character and _character._is_dashing:
		if animation_player.current_animation != "Jump":
			_play_animation("Jump")
		return

	# Check if on floor (works for any CharacterBody3D)
	var on_floor = _character.is_on_floor() if _character else true

	if not on_floor:
		if _velocity.y < 0:
			# Falling - use FallIdle animation
			_play_animation("FallIdle")
		else:
			_play_animation("Jump")
		return

	if _velocity:
		# Check if running (defensive check - enemies don't have is_running)
		var is_running_val = false
		if _character and _character.has_method("is_running"):
			is_running_val = _character.is_running() and on_floor
		elif _velocity.length() > 5.0:  # Fallback: high speed = running
			is_running_val = on_floor

		if is_running_val:
			# Sprint animation = Run for Lilguy
			_play_animation("Run")
			return

		# Walk animation for normal movement
		_play_animation("Walk")
		return

	# Idle - use FallIdle or Idle if it exists
	if animation_player.has_animation("Idle"):
		_play_animation("Idle")
	else:
		_play_animation("FallIdle")

func _play_animation(anim_name: String):
	if animation_player and animation_player.has_animation(anim_name):
		animation_player.play(anim_name)
	# Silently ignore if animation doesn't exist

func play_attack(anim_name: String = "Attack_OneHand") -> void:
	if animation_player:
		# Play attack animation once (don't loop)
		if animation_player.has_animation(anim_name):
			animation_player.play(anim_name, -1, 1.0)
			animation_player.animation_set_next(anim_name, "")
		else:
			# Fallback to default if animation doesn't exist
			#push_warning("Animation '%s' not found, using Attack_OneHand" % anim_name)
			animation_player.play("Attack_OneHand", -1, 1.0)
			animation_player.animation_set_next("Attack_OneHand", "")

## Apply hue shift to all mesh instances in the character
func set_character_color(hue_shift: float) -> void:
	# Find all MeshInstance3D children recursively
	var mesh_instances = _find_mesh_instances(self)

	for mesh in mesh_instances:
		_apply_hue_to_mesh(mesh, hue_shift)

## Recursively find all MeshInstance3D nodes
func _find_mesh_instances(node: Node) -> Array[MeshInstance3D]:
	var meshes: Array[MeshInstance3D] = []

	if node is MeshInstance3D:
		meshes.append(node)

	for child in node.get_children():
		meshes.append_array(_find_mesh_instances(child))

	return meshes

## Apply hue shift to a mesh instance
func _apply_hue_to_mesh(mesh_instance: MeshInstance3D, hue_shift: float) -> void:
	if not mesh_instance:
		return

	# Get or create material for each surface
	for i in range(mesh_instance.get_surface_override_material_count()):
		var material = mesh_instance.get_surface_override_material(i)

		# If no override material, get the base material and duplicate it
		if not material:
			material = mesh_instance.mesh.surface_get_material(i)
			if material:
				material = material.duplicate()
				mesh_instance.set_surface_override_material(i, material)

		# Apply hue shift if it's a StandardMaterial3D
		if material and material is StandardMaterial3D:
			var std_mat = material as StandardMaterial3D
			# Create a modulate color from hue
			var color = Color.from_hsv(hue_shift, 0.6, 1.0)
			std_mat.albedo_color = color
