extends CharacterBody2D

##week2 additions:
## states
## acceleration/deceleration
## hard gravity
## jump buffer
## shove attack

@onready var jump_buffer_timer: Timer = $jump_buffer_timer
@onready var coyote_timer: Timer = $coyote_timer
@onready var right_cast: RayCast2D = $RightCast
@onready var left_cast: RayCast2D = $LeftCast
@onready var playerGraphic: AnimatedSprite2D = $AnimatedSprite2D

const SPEED = 300.0
const JUMP_VELOCITY = -400.0
const PUSH_FORCE:int = 500

@export var bump_power = 50
@export var acceleration:int = 5 
@export var hard_gravity:int = 3

enum State{IDLE, RUN, JUMP, FALLING}
var current_state: State = State.IDLE

enum FaceDirection{LEFT, RIGHT}
var facing:FaceDirection = FaceDirection.RIGHT

var pushTarget:RigidBody2D
var pushEnabled:bool = false

func _physics_process(delta: float) -> void:

	handle_input()
	update_movement(delta)
	update_states()
	update_animation()
	move_and_slide()
	handle_collisions()

func handle_input() -> void:
	#Jumping action
	if Input.is_action_just_pressed("ui_accept"):
		jump_buffer_timer.start()
	if is_on_floor() && jump_buffer_timer.time_left > 0:
		velocity.y = JUMP_VELOCITY
		current_state = State.JUMP
		jump_buffer_timer.stop()
		
	#Walking action
	var direction := Input.get_axis("ui_left", "ui_right")
	#######REFACTOR################
	## Apply acceleration and deceleration to movement
	if direction == 0:
		velocity.x = move_toward(velocity.x, 0, acceleration)
	else:
		velocity.x = move_toward(velocity.x, SPEED * direction, acceleration)
		if direction <0:
			facing=FaceDirection.LEFT
			playerGraphic.flip_h = true
		else:
			facing = FaceDirection.RIGHT
			playerGraphic.flip_h = false
			
	if Input.is_action_just_pressed("shove") && pushEnabled:
		match facing:
			FaceDirection.RIGHT:
				#push right
				pushTarget.apply_central_impulse(Vector2(1,0) * PUSH_FORCE)
				pushEnabled = false
				
			FaceDirection.LEFT:
				#push left
				pushTarget.apply_central_impulse(Vector2(-1,0) * PUSH_FORCE)
				pushEnabled = false

##Add the hard gravity in here
func update_movement(delta:float)-> void:
	# Add the gravity.
	if current_state == State.JUMP:
		#apply normal gravity during up of jump action
		velocity += get_gravity() * delta
	else:
		#Character is falling
		velocity += get_gravity() * hard_gravity * delta
				
func update_states()->void:
	match current_state:
		State.IDLE when velocity.x !=0:
			current_state = State.RUN
		
		##player stops walking, or starts falling
		State.RUN:
			if velocity.x == 0:
				current_state = State.IDLE
			##player walks off of a ledge (no jump)
			if not is_on_floor() && velocity.y > 0:
				current_state = State.FALLING
				
		##when jump peaks, we start to fall	
		State.JUMP when velocity.y > 0:
			current_state = State.FALLING
		
		##player lands, either still or moving
		State.FALLING when is_on_floor():
			if velocity.x == 0:
				current_state = State.IDLE
			else:
				current_state = State.RUN

func update_animation()-> void:
	match current_state:
		State.IDLE:
			playerGraphic.play("idle")
		State.RUN:
			playerGraphic.play("run")
		State.JUMP:
			playerGraphic.play("jump")
		State.FALLING:
			playerGraphic.play("fall")
			
func handle_collisions()-> void:
	#react to objects around
	for i in get_slide_collision_count():
		var c = get_slide_collision(i)
		if c.get_collider() is RigidBody2D:
			c.get_collider().apply_central_impulse(-c.get_normal() * bump_power)
	
	if right_cast.is_colliding() && facing==FaceDirection.RIGHT:
		var collider = right_cast.get_collider()
		if collider is Node && collider is RigidBody2D:
			pushTarget = collider
			pushEnabled = true
			
	if left_cast.is_colliding() && facing==FaceDirection.LEFT:
		var collider = left_cast.get_collider()
		if collider is Node && collider is RigidBody2D:
			pushTarget = collider
			pushEnabled = true

	if not right_cast.is_colliding() and not left_cast.is_colliding():
		pushEnabled = false
