class_name Utils
extends Object


const VEC3_HOR := Vector3(1.0, 0.0, 1.0)


static func free_node_safely(node: Node) -> void:
	if is_instance_valid(node) and node.is_inside_tree():
		node.queue_free()


static func free_nodes(nodes: Array[Node]) -> void:
	for node: Variant in nodes:
		if is_instance_valid(node) and node is Node:
			Utils.free_node_safely(node)


static func validate_array(array: Array) -> Array:
	return array.filter(
		func(element: Variant) -> bool:
			if element == null or not is_instance_valid(element):
				return false

			if element is Node:
				return not element.is_queued_for_deletion()

			return true
	)


static func get_all_children(node: Node, internal: bool = false) -> Array[Node]:
	if not is_instance_valid(node):
		return []

	var children: Array[Node] = []
	for child: Node in node.get_children(internal):
		children.append(child)
		if child.get_child_count(internal) > 0:
			children.append_array(get_all_children(child, internal))

	return children


static func node_distance(
	node_a: Node3D, node_b: Node3D, position_modifier := Vector3.ONE
) -> float:
	if not is_instance_valid(node_a) or not is_instance_valid(node_b):
		return -1.0

	return (node_a.global_position * position_modifier).distance_to(
		node_b.global_position * position_modifier
	)


## Returns [code]true[/code] when [param node_a] is close to the [param from] node than [param node_b].[br]
## [b]Note:[/b] Meant to be used with the [method Array.sort_custom] function.
static func sort_by_distance(from: Node3D, node_a: Node3D, node_b: Node3D) -> bool:
	return Utils.node_distance(from, node_a) <= Utils.node_distance(from, node_b)


static func center_aabb(point: Vector3, aabb: AABB) -> AABB:
	return AABB(point + (aabb.get_center() - aabb.size), aabb.size)


## Returns [code]true[/code] if the given [param node] is of type
## [AudioStreamPlayer], [AudioStreamPlayer3D] or [AudioStreamPlayer2D].
static func is_node_audioplayer(node: Node) -> bool:
	return (
		is_instance_valid(node)
		and (
			node is AudioStreamPlayer
			or node is AudioStreamPlayer2D
			or node is AudioStreamPlayer3D
		)
	)


static func full_rotation(transform: Transform3D, direction: Vector3, delta: float) -> Transform3D:
	var old := Quaternion(transform.basis.orthonormalized()).normalized()
	var target := Basis(Vector3.UP, atan2(-direction.x, -direction.z))
	var new := Quaternion(target).normalized()
	var c: Quaternion = old.slerp(new, delta)
	transform.basis = Basis(c)
	return transform


static func full_rotation_float(
	transform: Transform3D, y_rotation: float, delta: float, rotation_vector := Vector3.UP
) -> Transform3D:
	var old := Quaternion(transform.basis).normalized()
	var target := Basis(rotation_vector, deg_to_rad(y_rotation))
	var new := Quaternion(target).normalized()
	var c: Quaternion = old.slerp(new, delta)
	transform.basis = Basis(c)
	return transform
