class_name Entity
extends CharacterBody2D

const FLASH_SPEED: = 0.125

signal died()

@export var bullet_prefab:PackedScene
@export var explode_prefab:PackedScene
@export var flash_sprite:Sprite2D

@export var speed:float = 90.0
@export var acceleration:float = 500.0
@export var rotate_speed_deg:float = 135.0
@export var fire_rate_secs:float = 0.5

@export var health:Health
@export var explode_color:Color = Color.WHITE

@export var fire_snd:AudioStreamPlayer2D
@export var push_snd:AudioStreamPlayer2D
@export var hurt_snd:AudioStreamPlayer2D
@export var arrow_hit_snd:AudioStreamPlayer2D
@export var die_snd:AudioStreamPlayer2D

var can_fire:bool:
	get: return bullet_prefab && fire_timer <= 0.0

var sm:FuncMachine = FuncMachine.new()
var fire_timer:float

var invunerable_timer:float

func _process(delta: float) -> void:
	sm.update(delta)
	if fire_timer > 0.0:
		fire_timer -= delta
	if invunerable_timer > 0.0:
		invunerable_timer -= delta

func _physics_process(delta: float) -> void:
	sm.fixed_update(delta)

var is_alive:bool = true:
	get: return is_alive
	
func rotate_to(node:Node2D, direction:Vector2, deg_per_sec:float = 0):
	var desired_rads:float = direction.angle() + PI / 2.0
	if deg_per_sec < 0.0:
		return
	elif deg_per_sec == 0.0:
		node.rotation = desired_rads
	else:
		node.rotation = lerp_angle(node.rotation, desired_rads, deg_to_rad(deg_per_sec))

func rotate_to_mouse(node:Node2D, min_rot:float = -360.0, max_rot:float = 360.0):
	var d:Vector2 = (get_global_mouse_position() - node.global_position).normalized()
	var desired_rads:float = d.angle() + PI / 2.0
	node.global_rotation = lerp_angle(node.global_rotation, desired_rads, deg_to_rad(45))
	node.rotation = clamp(node.rotation, deg_to_rad(min_rot), deg_to_rad(max_rot))

var _flash_tween:Tween
func _flash(enable:bool):
	if _flash_on(flash_sprite, enable) && enable:
		if _flash_tween:
			_flash_tween.kill()
		_flash_tween = create_tween()
		_flash_tween.tween_interval(FLASH_SPEED)
		_flash_tween.tween_callback(func(): _flash(false))

func _flash_on(sprite:Sprite2D, enable:bool) -> bool:
	if sprite:
		sprite.set_instance_shader_parameter("blink", 1.0 if enable else 0.0)
		return true
	return false

func push(vel_amount:Vector2):
	velocity += vel_amount
	play(push_snd)

func make_invincible(duration: float):
	invunerable_timer = max(0.0, duration)

func damage(amount: int):
	if amount == 0.0 || invunerable_timer > 0.0:
		return
		
	Event.damage_received.emit(-amount, global_position + Vector2.UP * 40)
	play(hurt_snd)
	health.modify(-amount)
	if health.value <= 0.0:
		is_alive = false
		died.emit()
		queue_free()
		if die_snd:
			remove_child(die_snd)
		
		var pf:CPUParticles2D = explode_prefab.instantiate()
		pf.color = explode_color
		pf.global_position = global_position
		if die_snd:
			pf.add_child(die_snd)
			play.call_deferred(die_snd)
			
		get_parent().add_child(pf)
	else:
		_flash(true)

func fire_bullet(start_position:Vector2, dir:Vector2) -> Bolt:
	if !can_fire:
		return null
	play(fire_snd)
	fire_timer = fire_rate_secs
	var b:Bolt = bullet_prefab.instantiate()
	b.global_position = start_position
	Event.fire_bullet.emit(b, dir)
	return b
	
func attach_bullet(bullet:Node2D):
	var original_transform:Transform2D = bullet.transform
	if bullet.get_parent():
		bullet.get_parent().remove_child(bullet)
	
	add_child(bullet)
	bullet.global_position = original_transform.origin
	bullet.global_rotation = original_transform.get_rotation()
	play(arrow_hit_snd)

func play(audio_str:AudioStreamPlayer2D):
	if audio_str && !audio_str.playing:
		audio_str.play()
