extends CharacterBody2D
class_name Player


## movement variables
@export var jump_height : float
@export var time_peak : float
@export var time_fall : float
@export var speed : float
@export var accel_time : float

## math
@onready var jump_vel : float = ((2.0 * jump_height) / time_peak) * -1.0
@onready var jump_grav : float = ((-2.0 * jump_height) / (time_peak * time_peak)) * -1.0
@onready var fall_grav : float = ((-2.0 * jump_height) / (time_fall * time_fall)) * -1.0
@onready var accel : float = speed / accel_time


## nodes
@onready var sprite : Sprite2D = $Sprite
@onready var jump_buffer : Timer = $JumpBuffer
@onready var coyote_timer : Timer = $CoyoteTimer
@onready var ledge_block : RayCast2D = $LedgeGrabRaycasts/HorizontalBlock
@onready var ledge_hor : RayCast2D = $LedgeGrabRaycasts/HorizontalRaycast
@onready var ledge_ver : RayCast2D = $LedgeGrabRaycasts/VerticalRaycast


## other variables
var grav_mult : float = 1.0
var direction : int = 0
var jump_buffered : bool = false
var can_jump : bool = true


func _physics_process(delta):

	## accelerate towards input direction

	velocity.x = move_toward(velocity.x, Input.get_axis('left', 'right') * speed, accel * delta)

	if Input.is_action_just_pressed('left'):
		sprite.flip_h = true
	
	if Input.is_action_just_pressed('right'):
		sprite.flip_h = false

	## adds gravity

	if not is_on_floor():
		velocity.y += get_grav() * grav_mult * delta

	## jump with jump buffering and coyote time

	if is_on_floor(): can_jump = true

	if not is_on_floor() and coyote_timer.is_stopped():
		coyote_timer.start()

	if Input.is_action_just_pressed('jump'):
		jump_buffered = true
		jump_buffer.start()

	if can_jump and jump_buffered: jump()


	if get_ledge_pos() != Vector2(0, 0):
		$LedgeMarkerTemp.global_position = get_ledge_pos()
		$LedgeMarkerTemp.visible = true
	else:
		$LedgeMarkerTemp.visible = false	

	
	## slow for testing
	if Input.is_action_just_pressed('slow'):
		if Engine.time_scale == 1:
			Engine.time_scale = 0.5
		else:
			Engine.time_scale = 1


	move_and_slide()


func jump() -> void:
	velocity.y = jump_vel


func get_grav() -> float:
	## makes it so you use fall gravity only if you're well falling
	return jump_grav if velocity.y < 0 else fall_grav


func get_grav_mult() -> float:
	if not Input.is_action_pressed('down'):
		return 0.5 if velocity.y in range(-20, 20) else 1.0
	else:
		return 1.5
	

func get_ledge_pos() -> Vector2:
	var ledge_pos = Vector2.ZERO

	$LedgeGrabRaycasts.scale.x = 1.0 if not sprite.flip_h else -1.0

	if ledge_hor.is_colliding():

		ledge_ver.global_position.x = ledge_hor.get_collision_point().x
		
		if not ledge_block.is_colliding():
			ledge_pos = ledge_ver.get_collision_point()
		if ledge_block.is_colliding():
			ledge_pos = Vector2.ZERO
	else:
		ledge_pos = Vector2.ZERO
	

	return ledge_pos


func _on_jump_buffer_timeout() -> void:
	jump_buffered = false


func _on_coyote_timeout() -> void:
	can_jump = false