class_name GameCamera extends CharacterBody3D

const SPRINT_MULT := 2.0
const PITCH_MAX := deg_to_rad(85.0)

var base_speed: float = ProjectSettings.get_setting("game/config/controls/camera/free_camera_speed")

var x_sensitivity: float = ProjectSettings.get_setting(
	"game/config/controls/camera/x_axis_sensitivity"
)
var x_acceleration: float = ProjectSettings.get_setting(
	"game/config/controls/camera/x_axis_acceleration"
)
var y_sensitivity: float = ProjectSettings.get_setting(
	"game/config/controls/camera/y_axis_sensitivity"
)
var y_acceleration: float = ProjectSettings.get_setting(
	"game/config/controls/camera/y_axis_acceleration"
)

var invert_pitch: bool = ProjectSettings.get_setting("game/config/controls/camera/invert_pitch")

@onready var _target := Vector2(rotation.x, rotation.y)


func _unhandled_input(event: InputEvent) -> void:
	if event is InputEventMouseButton:
		Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
	elif event is InputEventMouseMotion:
		if Input.get_mouse_mode() == Input.MOUSE_MODE_CAPTURED:
			camera_motion((event as InputEventMouseMotion).relative)


func camera_motion(motion: Vector2) -> void:
	_target.y = _target.y - deg_to_rad(motion.x * x_sensitivity)
	_target.x = clampf(
		_target.x - deg_to_rad(motion.y * y_sensitivity) * (-1 if invert_pitch else 1),
		-PITCH_MAX,
		PITCH_MAX,
	)


func _physics_process(delta: float) -> void:
	# Rotation
	rotation.y = lerp_angle(rotation.y, _target.y, delta * x_acceleration)
	rotation.x = lerp_angle(rotation.x, _target.x, delta * y_acceleration)

	# Handle spatial movement
	var xz_input := Input.get_vector("camera_left", "camera_right", "camera_forward", "camera_back")
	var y_input := Input.get_vector("", "", "camera_down", "camera_up")
	var direction := (transform.basis * Vector3(xz_input.x, y_input.y, xz_input.y)).normalized()

	var speed := base_speed
	if Input.is_action_pressed("camera_sprint"):
		speed *= SPRINT_MULT

	if direction:
		velocity = direction * speed
	else:
		velocity = velocity.move_toward(Vector3.ZERO, speed)

	move_and_slide()
