class_name Player
extends Entity

# For the right arm. Negate for the left arm
const MAX_ARM_ROT_DEG :float = 80.0
const MIN_ARM_ROT_DEG :float = -25.0
const MAX_BOLTS := 10

@export var to_grapple_speed:float = 400.0
@export var grapple_slide_damage := 2.0

@export var right_arm:Sprite2D
@export var left_arm:Sprite2D
@export var grapple:GrappleGun

@onready var body: Sprite2D = $Visuals/Body
@onready var head: Sprite2D = $Visuals/Head

@onready var visuals: Node2D = $Visuals
@onready var load_point: Marker2D = $Visuals/L_Arm/Bow/LoadPoint
@onready var collider: CollisionShape2D = $CollisionShape2D
@onready var anim: AnimationPlayer = $Anim
@onready var step_snd: AudioStreamPlayer2D = $Step

var bolts:ObservableInt = ObservableInt.new(5)
var ammo:int:
	get: return bolts.value
	set(val): bolts.value = val

var time_since_last_bolt:float

func _ready():
	sm.add(Callable(), _normal_update, _normal_exit)
	sm.add(_to_grapple_enter, _to_grapple_update)
	sm.start(_normal_update)
	add_to_group("player")
	grapple.retrieved.connect(_on_grapple_retrieved)

func _process(delta: float) -> void:
	super._process(delta)
	time_since_last_bolt += delta

func _physics_process(delta: float) -> void:
	super._physics_process(delta)
	collider.rotation = visuals.rotation - PI / 2.0
	
	# I can rotate the characterbody2D as long as i do it from _physics_process()
	#var d:Vector2 = (get_global_mouse_position() - global_position)
	#rotate_to(self, d, rotate_speed_deg * delta)
	
	if move_and_slide():
		var collision:KinematicCollision2D = get_slide_collision(0)
		var other_coll:CollisionObject2D = collision.get_collider()
		var c_layer:int = other_coll.collision_layer
		
		if sm.current_state_name == "normal":
			velocity = velocity.bounce(collision.get_normal()) * .8
			
		elif c_layer == C.LYR_MASK_ENEMY && sm.current_state_name == "to_grapple" && grapple.is_attached:
			grapple.retract()
			other_coll.damage(grapple_slide_damage)
			make_invincible(0.25)
			sm.change(_normal_update)

func _normal_update(delta):
	var d:Vector2 = (get_global_mouse_position() - global_position)
	rotate_to(visuals, d, rotate_speed_deg * delta)
	
	rotate_to_mouse(right_arm, MIN_ARM_ROT_DEG, MAX_ARM_ROT_DEG)
	rotate_to_mouse(left_arm, -MAX_ARM_ROT_DEG, -MIN_ARM_ROT_DEG)
	
	var dir:Vector2 = get_dir()
	if dir == Vector2.ZERO:
		velocity = velocity.move_toward(Vector2.ZERO, acceleration * delta)
		anim.play("idle")
	else:
		velocity = velocity.move_toward(dir * speed, acceleration * delta)
		anim.play("walk")
		#rotate_to(visuals, dir, rotate_speed * delta)
	
	if grapple.is_attached:
		sm.change(_to_grapple_enter)
		return
				
	if grapple.is_ready && Input.is_action_just_pressed("grapple"):
		grapple.fire()
	elif Input.is_action_just_released("grapple"):
		grapple.retract()
	elif Input.is_action_just_pressed("fire") && ammo > 0:
		var b:Bolt = fire_bullet(load_point.global_position, load_point.global_transform.x.rotated(-PI / 2.0))
		if b:
			ammo -= 1
func _normal_exit():
	anim.play("idle")
	
func _to_grapple_enter():
	if grapple.is_retracted:
		grapple.reset()
		sm.change(_normal_update)

func _to_grapple_update(delta:float):
	rotate_to(visuals, grapple.dir_to_grapple, rotate_speed_deg * delta)
	
	if grapple.is_retracted:
		grapple.reset()
		sm.change(_normal_update)
	else:
		velocity = grapple.dir_to_grapple * to_grapple_speed
		if Input.is_action_just_pressed("cancel_to_grapple"):
			grapple.retract()
			sm.change(_normal_update)
		
func get_dir() -> Vector2:
	var dx = Input.get_action_strength("right") -  Input.get_action_strength("left")
	var dy = Input.get_action_strength("down") -  Input.get_action_strength("up")
	return Vector2(dx, dy).normalized()

func attach_bullet(bullet:Node2D):
	bullet.queue_free()
	
func step():
	play(step_snd)
	
func _flash_on(spr:Sprite2D, enable:bool) -> bool:
	body.set_instance_shader_parameter("blink", 1.0 if enable else 0.0)
	head.set_instance_shader_parameter("blink", 1.0 if enable else 0.0)
	left_arm.set_instance_shader_parameter("blink", 1.0 if enable else 0.0)
	right_arm.set_instance_shader_parameter("blink", 1.0 if enable else 0.0)
	head.set_instance_shader_parameter("blink", 1.0 if enable else 0.0)
	return true

func _on_grapple_retrieved(sp:Sprite2D):
	print("TOOD: Make this better!")
	ammo = min(MAX_BOLTS, ammo + 1)
