extends CharacterBody3D


const _BOB_FREQUENCY = 1.5
const _BOB_AMPLITUDE = 0.1

@export var _pivot: Node3D
@export var _camera: Camera3D

var interact_target: Node3D

var _gravity := -30
var _max_speed := 12
var _mouse_sensitivity := 0.008  # radians/pixel
var _time_bob = 0.0

var _interact_pool: Array[Node3D]


func _ready() -> void:
	pass


func _unhandled_input(event: InputEvent) -> void:
	if GameState.fetch().is_player_input_disabled:
		return
	
	if event is InputEventMouseMotion and Input.get_mouse_mode() == Input.MOUSE_MODE_CAPTURED:
		rotate_y(-event.relative.x * _mouse_sensitivity)
		#pivot.rotate_x(-event.relative.y * mouse_sensitivity)
		#pivot.rotation.x = clamp(pivot.rotation.x, -1.2, 1.2)
	
	if event.is_action_pressed("interact") and is_instance_valid(interact_target):
		var comps := Components.get_from(interact_target)
		var c_interactable := comps.get_component("CInteractable") as CInteractable
		c_interactable.interact(self)


func _process(delta: float) -> void:
	_apply_view_bobbing(delta)
	_compute_interact_target()


func _physics_process(delta: float) -> void:
	velocity.y += _gravity * delta
	var desired_velocity := _get_movement_input() * _max_speed
	
	velocity.x = desired_velocity.x
	velocity.z = desired_velocity.z
	move_and_slide()


func _get_movement_input() -> Vector3:
	if GameState.fetch().is_player_input_disabled:
		return Vector3.ZERO
	
	var input_dir := Vector3()
	
	# desired move in camera direction
	if Input.is_action_pressed("move_forward"):
		input_dir += -global_transform.basis.z
	if Input.is_action_pressed("move_backward"):
		input_dir += global_transform.basis.z
	if Input.is_action_pressed("strafe_left"):
		input_dir += -global_transform.basis.x
	if Input.is_action_pressed("strafe_right"):
		input_dir += global_transform.basis.x
	
	return input_dir.normalized()


func _apply_view_bobbing(delta: float) -> void:
	_time_bob += delta * velocity.length() * float(is_on_floor())
	_camera.position.y = sin(_time_bob * _BOB_FREQUENCY) * _BOB_AMPLITUDE
	_camera.position.x = cos(_time_bob * (_BOB_FREQUENCY / 2)) * _BOB_AMPLITUDE


func _compute_interact_target() -> void:
	interact_target = _compute_nearest(_interact_pool)


func _compute_nearest(things: Array[Node3D]) -> Node3D:
	var lowest_distance := INF
	var closest: Node3D
	
	for thing in things:
		var distance := global_position.distance_squared_to(thing.global_position)
		
		if distance < lowest_distance:
			closest = thing
			lowest_distance = distance
	
	return closest


func _on_interact_area_body_entered(body: Node3D) -> void:
	var comps := Components.get_from(body)
	if comps == null:
		return
	
	var c_interactable := comps.get_component("CInteractable") as CInteractable
	if c_interactable == null:
		return
	
	_interact_pool.push_back(body)


func _on_interact_area_body_exited(body: Node3D) -> void:
	_interact_pool.erase(body)
