class_name PlayerCharacter
extends Character3D


const ACTION_RUN: StringName = &"run"
const ACTION_JUMP: StringName = &"jump"
const ACTION_CROUCH: StringName = &"crouch"
const ACTION_MOVE_LEFT: StringName = &"move_left"
const ACTION_MOVE_RIGHT: StringName = &"move_right"
const ACTION_MOVE_FORWARD: StringName = &"move_forward"
const ACTION_MOVE_BACKWARD: StringName = &"move_backward"

@export_group("Crouching")
@export var crouch_walk_speed_multiplier: float = 0.4
@export var crouch_speed: float = 7.25
@export var standup_speed: float = 5.3
@export var standup_speed_affection: float = 0.5
@export var standup_speed_affection_speed_multiplier: float = 0.25
@export var standing_collision_height: float = 1.8
@export var standing_camera_height: float = 1.6
@export var crouch_collision_height: float = 0.9
@export var crouch_camera_height: float = 0.81

var has_control: bool = true
var is_crouching: bool = false
var crouch_speed_affection: float = 0.0

@onready var head: PlayerHead = $Head
@onready var collision_shape: CollisionShape3D = $CylinderCollider
@onready var standup_checker: Area3D = $StandupChecker
@onready var stair_stepper: StairStepper = $StairStepper


func _ready() -> void:
	GameGlobals.player = self

	#if not get_tree().current_scene.is_node_ready():
		#await get_tree().current_scene.ready
#
	#var spawn_point := PlayerSpawnPoint.get_spawn_point_by_identifier(GameGlobals.spawn_id)
#
	#if is_instance_valid(spawn_point):
		#global_position = spawn_point.global_position
		#head.global_rotation = spawn_point.global_rotation


func _physics_process(delta: float) -> void:
	if GameGlobals.in_cutscene:
		return

	# Add the gravity.
	if not is_on_floor():
		apply_gravity(delta)
	elif not _was_on_floor:
		if previous_velocity.y <= -2.0:
			head.camera.shake(0.025, 0.175)

	if not has_control:
		move(Vector3.ZERO, delta)
		move_and_stair_step()

		#if is_on_floor():
			#stair_stepper.handle_step_climbing()
		return

	# Handle jump.
	if Input.is_action_just_pressed(ACTION_JUMP) and is_on_floor() and not is_crouching:
		jump()

	handle_crouching(delta)

	var input_dir: Vector3 = get_input_direction()
	movement_direction = input_dir.normalized()

	# Rotate the movement direction based on the cameras orientation.
	movement_direction = movement_direction.rotated(Vector3.UP, head.global_rotation.y)

	speed = get_desired_speed() * input_dir.length()
	speed *= crouch_walk_speed_multiplier if is_crouching else 1.0

	if not is_crouching:
		speed *= remap(crouch_speed_affection, 0.0, 1.0, 1.0, standup_speed_affection)

	move(movement_direction, delta)
	move_and_stair_step()

	SPrint.print_msgf(
		"Player Horizontal-Velocity: %s\nPlayer Vertical-Velocity: %s"
		% [(velocity * Utils.VEC3_HOR).length(), velocity.y], true
	)

	#if is_on_floor():
		#stair_stepper.handle_step_climbing()


func apply_gravity(delta: float) -> void:
	super(delta)


func get_input_direction() -> Vector3:
	if not InputManager.window_focused():
		return Vector3.ZERO

	var input_dir := Input.get_vector(ACTION_MOVE_LEFT, ACTION_MOVE_RIGHT, ACTION_MOVE_FORWARD, ACTION_MOVE_BACKWARD)
	var direction: Vector3 = (transform.basis * Vector3(input_dir.x, 1, input_dir.y))#.normalized()
	direction.y = 0.0
	return direction


func get_desired_speed() -> float:
	if Input.is_action_pressed(ACTION_RUN) and not is_crouching:
		return movement.running_speed if is_on_floor() else movement.air_running_speed

	return movement.walking_speed if is_on_floor() else movement.air_walking_speed


func handle_crouching(delta: float) -> void:
	var should_crouch: bool = Input.is_action_pressed(ACTION_CROUCH) and (is_on_floor() or is_crouching)
	var would_ceiling_collide: bool = standup_checker.has_overlapping_bodies()

	is_crouching = should_crouch or would_ceiling_collide
	var crouching_speed: float = crouch_speed if is_crouching else standup_speed

	var target_collision_height: float = crouch_collision_height if is_crouching else standing_collision_height
	collision_shape.shape.height = target_collision_height #lerp(collision_shape.shape.height, target_collision_height, crouching_speed * delta * 5.0)
	collision_shape.position.y = collision_shape.shape.height / 2.0

	var target_camera_height: float = crouch_camera_height if is_crouching else standing_camera_height
	var weight: float = 1.0 - pow(0.5, delta * crouching_speed)
	head.position.y = lerp(head.position.y, target_camera_height, weight)

	crouch_speed_affection = lerp(crouch_speed_affection, float(is_crouching), weight * standup_speed_affection_speed_multiplier if not is_crouching else 1.0)

	if crouch_speed_affection <= 0.05:
		crouch_speed_affection = 0.0

	SPrint.print_msgf("Crouching Speed Affection: %s\nIs Crouching: %s" % [crouch_speed_affection, is_crouching], true)
