extends CharacterBody2D

@export_range(10, 1000, 10, "or_greater")
var speed = 300.0
@export_range(0, 0.2, 0.001, "or_greater")
var acceleration = 0.5

@export_group("Airborne")
@export_range(0, 1000, 10, "or_greater")
var jump_velocity = 400.0
@export_range(0, 10, 0.1, "or_greater")
var gravity_modifier = 1

@onready var hitting: Timer = $Hitting
@onready var hitbox: Area2D = $Hitbox

@onready var armor: AnimatedSprite2D = $Armor
@onready var sword: AnimatedSprite2D = $Sword
@onready var damageable: Damageable = $Damageable
@onready var shield: AnimatedSprite2D = $Shield
@onready var base: AnimatedSprite2D = $Base
var current_sprite: AnimatedSprite2D

var is_in_cutscene = true  # back to true on build
var current_animation = "idle"

var is_hitting = false
var has_shield = false
var has_sword = false
var has_armor = false

@onready var steps_player: AudioStreamPlayer = $StepsPlayer
@onready var gearup_armor: AudioStreamPlayer = $GearupArmor
@onready var gearup_sword: AudioStreamPlayer = $GearupSword
@onready var gearup_shield: AudioStreamPlayer = $GearupShield
@onready var sword_hit: AudioStreamPlayer = $SwordHit
@onready var got_hit_audio: AudioStreamPlayer = $GotHitAudio


func play_anim():
	current_sprite.play(current_animation)

func play_anim_run():
	current_animation = "run"
	current_sprite.play("run")

func play_anim_idle():
	current_animation = "idle"
	current_sprite.play("idle")
	
func play_anim_jump():
	current_animation = "walk"
	current_sprite.play("walk")
	
func play_anim_dance():
	current_animation = "dance"
	current_sprite.play("dance")
		
func set_in_cutscene():
	is_in_cutscene = true
	play_anim_idle()
	
func set_in_play():
	is_in_cutscene = false
	
func look_left():
	current_sprite.flip_h = true
	
func look_right():
	current_sprite.flip_h = false
	
func _ready() -> void:
	current_sprite = base

func _physics_process(delta: float) -> void:
	if is_in_cutscene:
		velocity.x = move_toward(velocity.x, 0, speed)
		move_and_slide()
		return
	play_anim()
	
	if is_hitting:
		current_animation = "hit"
		velocity.x = move_toward(velocity.x, 0, speed)
		move_and_slide()
		return
	
	if not is_on_floor():
		velocity += get_gravity() * delta * gravity_modifier

	var direction := Input.get_axis("move_left", "move_right")
	
	if(has_shield and not has_sword and direction == 0):
		damageable.set_active(false)
	else:
		damageable.set_active(true)
	
	if direction > 0 and is_on_floor():
		look_right()
	if direction < 0 and is_on_floor():
		look_left()
	
	if not is_on_floor():
		current_animation = "got_hit"
	else:
		if velocity.x !=  0:
			play_anim_run()
		else:
			play_anim_idle()
	
	if is_on_floor():
		if direction:
			var smoothed_speed = speed * smoothstep(0, 1, acceleration)
			velocity.x += direction * smoothed_speed
			velocity.x = clampf(velocity.x, -speed, speed)
		else:
			velocity.x = move_toward(velocity.x, 0, speed)
	
	if Input.is_action_just_pressed("hit") and has_sword:
		hit()
		
	move_and_slide()

func hit():
	is_hitting = true
	sword_hit.play()
	hitting.start()
	hitbox.monitoring = true

func _on_hitting_timeout() -> void:
	is_hitting = false
	hitbox.monitoring = false
	damageable.set_active(true)

func _on_dialogue_manager_dialogue_ended() -> void:
	set_in_play()

func _on_trigger_dialogue_body_entered(body: Node2D) -> void:
	set_in_cutscene()

func _on_start_dancing() -> void:
	play_anim_dance()

func _on_npc_shield_dialogue_dialogue_ended() -> void:
	set_in_play()
	current_sprite.visible = false
	current_sprite = shield
	current_sprite.play("idle")
	current_sprite.visible = true
	has_shield = true
	gearup_shield.play()

func _on_red_hood_sword_dialogue_dialogue_ended() -> void:
	set_in_play()
	current_sprite.visible = false
	current_sprite = sword
	current_sprite.play("idle")
	current_sprite.visible = true
	has_sword = true
	gearup_sword.play()

func _on_put_on_armor() -> void:
	gearup_armor.play()
	current_sprite.visible = false
	current_sprite = armor
	current_sprite.play("power_up")
	current_sprite.visible = true
	has_armor = true

func _on_final_armor_dialogue_ended() -> void:
	set_in_play()
	current_sprite.play("idle")

func _on_trigger_final_dialogue_body_entered(body: Node2D) -> void:
	set_in_cutscene()


func _on_step_sound_interval_timeout() -> void:
	if velocity.x == 0:
		return
	if not is_on_floor():
		return
	steps_player.play()


func _on_damageable_got_hit() -> void:
	got_hit_audio.play()
