class_name Player
extends CharacterBody2D

@onready var sprite := $Sprite as AnimatedSprite2D
@onready var audio_player := $AudioPlayer as AudioStreamPlayer

signal state_change(old: States, new: States)

enum States {NULL, IDLE, RUN_START, RUN, ROLL, JUMP_START, JUMP, FALL, LAND, DAMAGE, RAGDOLL, BOUNCE, LAY, SHOOT, JAB, STRAIGHT, KICK, POUND, POUND_FALL}

const STATES_IMMOBILE = [States.ROLL, States.DAMAGE, States.RAGDOLL, States.BOUNCE, States.LAY, States.SHOOT, States.JAB, States.STRAIGHT, States.KICK, States.POUND, States.POUND_FALL]
const STATES_MOVEMENT = [States.RUN_START, States.RUN, States.JUMP_START, States.JUMP, States.FALL]
const STATES_ATTACK = [States.JAB, States.STRAIGHT, States.KICK, States.POUND]
const STATES_AIRBORNE = [States.JUMP_START, States.JUMP, States.FALL]

# FX
const ROLL_DUST = preload("uid://botc600n5fcgt")
const JUMP_DUST = preload("uid://c4h1cj75b5sry")

# Sounds
const JUMP_SOUND = preload("uid://d1hwunwdel6sy")
const HIT_GROUND = preload("uid://ciglwfq0vua8i")
const POUND_SOUND = preload("uid://css0nv2dxb52w")

# Projectiles
const POUND_BULLET = preload("uid://5uiinhmeyov0")

const SPEED = 90.0
const ACCEL = 450
const JUMP_VELOCITY = -312.0
const ROLL_VELOCITY = 150.0
const ROLL_DECEL = 1080

var state: States = States.IDLE: set = _set_state
var state_buffer: States = States.NULL
var direction: float

func _set_state(s: States):
	state_change.emit(state, s)
	state = s

func _process(_delta: float) -> void:
	$State.text = States.keys()[state]

func _physics_process(delta):
	# Add the gravity.
	if not is_on_floor():
		velocity += get_gravity() * delta
	
	# Jumping
	if state not in STATES_IMMOBILE and state not in STATES_AIRBORNE and Input.is_action_just_pressed("jump") and is_on_floor():
		state = States.JUMP_START
	
		audio_player.stream = JUMP_SOUND
		audio_player.play()
		
		var jump_dust = JUMP_DUST.instantiate()
		jump_dust.position = position
		add_sibling(jump_dust)
		
		velocity.y = JUMP_VELOCITY
	
	# Short hops
	if state in [States.JUMP_START, States.JUMP] and Input.is_action_just_released("jump") and !is_on_floor():
		velocity.y = velocity.y / 2
	
	# Falling
	if state in [States.IDLE] + STATES_MOVEMENT and velocity.y > 0:
		state = States.FALL
	
	# Landing
	if state in [States.FALL] and is_on_floor():
		if direction:
			state = States.RUN
		else:
			state = States.LAND

	# Rolls
	if state not in STATES_IMMOBILE and state != States.ROLL and \
		(Input.is_action_just_pressed("roll_jump") or (Input.is_action_just_pressed("roll") and direction)):
		state = States.ROLL
		
		if is_on_floor():
			var roll_dust = ROLL_DUST.instantiate()
			roll_dust.position = position
			roll_dust.flip_h = !sprite.flip_h
			add_sibling(roll_dust)
		
		if !sprite.flip_h:
			velocity.x = ROLL_VELOCITY
		else:
			velocity.x = -ROLL_VELOCITY
		
		if velocity.y >= 0 && Input.is_action_just_pressed("roll_jump"):
			velocity.y = -120
	
	# Melee
	if state not in STATES_IMMOBILE and Input.is_action_just_pressed("melee"):
		if Input.is_action_pressed("roll"):
			state = States.POUND
			velocity.x = 0
		elif state in STATES_AIRBORNE:
			state = States.KICK
		else:
			state = States.JAB
	
	# Pound
	if state in [States.POUND_FALL] and is_on_floor():
		state = States.POUND
	
	# Shooting
	#if state not in STATES_IMMOBILE and Input.is_action_just_pressed("shoot"):
		#state = States.SHOOT
		#match global.save.projectile:
			#global.save.Projectile.BLAST:
				#pass
	
	# Ragdoll
	if state not in [States.RAGDOLL, States.BOUNCE, States.LAY] and Input.is_action_just_pressed("ragdoll"):
		state = States.RAGDOLL
	
	if state in [States.RAGDOLL] and is_on_floor():
		state = States.BOUNCE
		
		var roll_dust = ROLL_DUST.instantiate()
		roll_dust.position = position
		roll_dust.flip_h = !sprite.flip_h
		add_sibling(roll_dust)
		
		velocity.y = -120
		audio_player.stream = HIT_GROUND
		audio_player.play()
	
	if state in [States.BOUNCE] and is_on_floor() and velocity.y >= 0:
		state = States.LAY
		
		audio_player.stream = HIT_GROUND
		audio_player.play()
	
	if state in [States.LAY] and is_on_floor():
		velocity.x = move_toward(velocity.x, 0, ACCEL * delta)

	# Overall physics
	direction = Input.get_axis("move_left", "move_right")
	if state == States.ROLL and sprite.frame > 7:
		# Slow down if in the later roll frames.
		velocity.x = move_toward(velocity.x, 0, ROLL_DECEL * delta)
	elif state not in STATES_IMMOBILE:
		# Apply velocity changes if not in a restricted movement state.
		velocity.x = move_toward(velocity.x, direction * SPEED, ACCEL * delta)
		if direction:
			if state not in STATES_MOVEMENT:
				state = States.RUN_START
		else:
			if state not in (STATES_AIRBORNE + [States.LAND]):
				state = States.IDLE

	move_and_slide()
