class_name PlayerAttack
extends Area3D

signal attacked

enum Side { RIGHT, LEFT }

@export_group("Collision")
@export var _collision_debug_material: Material
@export var _attack_max_angle: float = 2 * PI / 3
@export var attack_radius: float = 2

@export_group("Timers")
@export var _cooldown_time: float = 0.3
@export var _hit_window_time: float = 0.15

@export_group("Hits")
@export var _hit_projectile_speed: float = 35
@export var _direction_angles: Dictionary = {0.0: 0.0, PI: PI / 2.0}

var side := Side.RIGHT

var _debug_collision_shapes := DebugCollisionShapes.new()

var _cooldown_timer: float
var _hit_window_timer: float

@onready var _attack_shape_node: CollisionShape3D = $AttackShape

@onready var _attack_shape: CylinderShape3D = _attack_shape_node.shape as CylinderShape3D


func _ready() -> void:
	Debugger.add_event("attacked")
	attacked.connect(func() -> void: Debugger.event_emitted("attacked", []))
	area_entered.connect(_on_area_entered)

	position.y = Projectile.HEIGHT
	_set_collision_size(attack_radius)

	_debug_collision_shapes.init(get_children(), self, _collision_debug_material)


func _process(delta: float) -> void:
	if _cooldown_timer > 0:
		_cooldown_timer -= delta

	if _hit_window_timer > 0:
		_hit_window_timer -= delta
	elif _debug_collision_shapes.is_visible:
		_debug_collision_shapes.set_visibility(false)

	Debugger.text("_cooldown_timer", _cooldown_timer, 2)
	Debugger.text("_hit_window_timer", _hit_window_timer, 2)
	Debugger.vector(
		"fghdh",
		global_position,
		(
			global_position
			+ global_basis.z.rotated(Vector3.UP, _attack_max_angle) * attack_radius
		)
	)
	Debugger.vector(
		"fghdh2",
		global_position,
		(
			global_position
			+ global_basis.z.rotated(Vector3.UP, -_attack_max_angle) * attack_radius
		)
	)
	for dir_angle: float in _direction_angles.keys():
		Debugger.line(
			"fghdh3" + str(dir_angle),
			global_position,
			(
				global_position
				+ global_basis.z.rotated(Vector3.UP, dir_angle) * attack_radius
			),
			Color.BLUE
		)


func _physics_process(_delta: float) -> void:
	monitoring = _hit_window_timer > 0


func is_hitting() -> bool:
	return _cooldown_timer > 0


func attack() -> void:
	if _cooldown_timer > 0:
		return

	_cooldown_timer = _cooldown_time
	_hit_window_timer = _hit_window_time
	_debug_collision_shapes.set_visibility(true)
	side = Side.LEFT if side == Side.RIGHT else Side.RIGHT
	attacked.emit()


func _hit_projectile(projectile: Projectile) -> void:
	var diff := projectile.global_position - global_position
	diff.y = 0
	var angle := global_basis.z.signed_angle_to(diff, Vector3.UP)
	Debugger.vector("ASDSAD", global_position, global_position + global_basis.z)
	Debugger.vector("ASDSAD2", global_position, global_position + diff)
	Debugger.text("angle", rad_to_deg(angle), 2)
	if angle > _attack_max_angle or angle < -_attack_max_angle:
		return

	var angle_sign := -1.0 if side == Side.RIGHT else 1.0

	var angle_signed := angle * angle_sign
	Debugger.text("side", Side.find_key(side), 2)
	Debugger.text("angle_signed", rad_to_deg(angle_signed), 2)

	var prev_dir_angle: float = -_attack_max_angle
	for dir_angle: float in _direction_angles.keys():
		if angle_signed > prev_dir_angle and angle_signed <= dir_angle:
			Debugger.text("prev_dir_angle", rad_to_deg(prev_dir_angle), 2)
			Debugger.text("dir_angle", rad_to_deg(dir_angle), 2)
			var new_direction := global_basis.z.rotated(
				Vector3.UP, (_direction_angles[dir_angle] as float) * angle_sign
			)
			Debugger.vector(
				"ASDSAD3",
				projectile.global_position,
				projectile.global_position + new_direction
			)
			projectile.set_velocity(new_direction * _hit_projectile_speed)
			break

		prev_dir_angle = dir_angle


func _set_collision_size(radius: float) -> void:
	_attack_shape.radius = radius


func _on_area_entered(node: Node3D) -> void:
	if _hit_window_timer <= 0:
		return

	if node is Projectile:
		_hit_projectile(node as Projectile)
