extends KinematicBody2D

const MAX_SPEED = 90
const WALL_SPEED = 40
const ROLL_SPEED = 150
const FRICTION = 50
const ACCEL = 50
const ROLL_ADJUST_SPEED = 35

const ROLL_DURATION = 0.5
const ROLL_COOLDOWN = 0.16
const SKID_DURATION = 0.25

var velocity = Vector2.ZERO

var speed = MAX_SPEED
var objectdist = 25
var walldist = 5
var safedist = 4

var deadzone = 0.25

var heal_rate = 1.5

var walksprites = ["walkup","walkdown","walkright","walkright"]
var idlesprites = ["idleup","idledown","idleright","idleright"]
var rollsprites = ["rollup","rolldown","rollright","rollright"]
var skidsprites = ["skidup","skiddown","skidright","skidright"]
var bonksprites = ["bonkup","bonkdown","bonkright","bonkright"]
var holdsprites = ["holdup","holddown","holdright","holdright"]
var holdwalksprites = ["walkup","walkdown","holdwalkright","holdwalkright"]
var throwsprites = ["walkup","walkdown","throwright","throwright"]

var flask = preload("res://objects/objects/flask/Flask.tscn")

enum dirs {
	UP,
	DOWN,
	LEFT,
	RIGHT
}

enum {
	WALKING,
	WALL,
	DEAD,
	FALLING,
	FREEFALL,
	ROLL,
	SKID,
	BONK,
	GRAB,
	THROW
}

var currentdir = dirs.DOWN
var currentstate = WALKING

var wallinit = false
var rollinit = false
var skidinit = false
var throwinit = false
var moveaxis = Vector2.ZERO

var stored_velocity = Vector2.ZERO

var joymode = false

enum items {
	FLASK,
	BOX
}

var holding = false
var helditem = null
var rollenabled = true


func _ready():
	$Flashlight.visible = extstd.get_world_brightness()

func move(top_speed,top_accel,top_friction,delta,custom_axis=null,iced=false,wallmode=false,walltype=dirs.DOWN):
	delta *= 60
	if iced:
		top_friction = top_friction/20
		top_accel = top_accel/8
	
	var input_vector = Vector2.ZERO
	if custom_axis == null:
		input_vector = get_input_axis()
	else:
		input_vector = custom_axis
	
	if input_vector != Vector2.ZERO:
		velocity += input_vector * top_accel * delta
		velocity = velocity.clamped(top_speed)
	else:
		velocity = velocity.move_toward(Vector2.ZERO,top_friction*delta)

# warning-ignore:return_value_discarded
	move_and_slide(velocity)
	return input_vector


func get_input_axis(): #Get up/down and left/right and normalize the result
	var axis = Vector2.ZERO
	axis.x = int(Input.get_action_strength("ui_right")) - int(Input.get_action_strength("ui_left"))
	axis.y = int(Input.get_action_strength("ui_down")) - int(Input.get_action_strength("ui_up"))
	
	if Input.get_joy_axis(0,0) != 0 or Input.get_joy_axis(0,1) != 0:
		axis = Vector2(Input.get_joy_axis(0,0),Input.get_joy_axis(0,1))
		if abs(axis.x) < deadzone: axis.x = 0
		if abs(axis.y) < deadzone: axis.y = 0
		joymode = true
	else:
		joymode = false
	
	$Timers/RegenTimer.wait_time = 0.5 + int(axis != Vector2.ZERO)
	
	return axis


func wall_cast(wallcast:Vector2,interactcast:Vector2): #Cast raycasts while on the wall depending on direction
	$WallCast1.cast_to = wallcast
	$WallCast2.cast_to = wallcast
	$Interact.cast_to = interactcast
	if !wall_check():
		leave_wall()

	
func _physics_process(delta):
	match currentstate:
		WALKING:	
			$Collision.scale = Vector2(1,1)
			$Shadow.offset = Vector2(0,0)
			$Anim.offset = Vector2(0,0)
			var axis = move(speed,ACCEL,FRICTION,delta)
			if axis == Vector2.ZERO:
				$helditem/Anim.play("IdleRight")
				if !holding:
					$Anim.animation = idlesprites[currentdir]
				else:
					$Anim.animation = holdsprites[currentdir]
			else:
				stored_velocity = get_input_axis()
				if !$Anim.is_playing():
					$Anim.frame = 1
				$Anim.playing = true
				
				if !holding:
					$Anim.animation = walksprites[currentdir]
				else:
					$Anim.animation = holdwalksprites[currentdir]
					$helditem/Anim.play("WalkRight")
				if axis.y > 0 and (axis.x <= 0.4 and axis.x >= -0.4):
					$Anim.flip_h = false
					currentdir = dirs.DOWN
					$WallCheck.cast_to = Vector2(0,walldist)
					$Interact.cast_to = Vector2(0,objectdist)
					$Shadow.offset.x = 0
				if axis.y < 0 and (axis.x <= 0.4 and axis.x >= -0.4):
					$Anim.flip_h = false
					currentdir = dirs.UP
					$WallCheck.cast_to = Vector2(0,-walldist-1)
					$Interact.cast_to = Vector2(0,-objectdist)
					$Shadow.offset.x = 0
				if axis.x > deadzone+0.015:
					$Anim.flip_h = false
					currentdir = dirs.RIGHT
					$WallCheck.cast_to = Vector2(walldist+2,0)
					$Interact.cast_to = Vector2(objectdist,0)
					$Shadow.offset.x = -3
				if axis.x < -deadzone-0.015:
					$Anim.flip_h = true
					currentdir = dirs.LEFT
					$WallCheck.cast_to = Vector2(-walldist-1,0)
					$Interact.cast_to = Vector2(-objectdist,0)
					$Shadow.offset.x = 2
				
			if wall_check() and (axis.x != 0 or axis.y != 0):
				if !wallinit:
					$WallTimer.set_wait_time(0.3)
					$WallTimer.start()
					wallinit = true
		
		
		WALL:
			if !wallinit:
				print("on wall")
				wallinit = true
				$WallCast1.enabled = true
				$WallCast2.enabled = true
				
			$Anim.playing = true
			var axis = move(WALL_SPEED,ACCEL,FRICTION,delta,null,false,true,currentdir)
			if currentdir == dirs.DOWN:
				$Anim.offset.y = 2
				wall_cast(Vector2(0,-safedist),Vector2(0,-objectdist))
				if axis.x > 0:
					$Anim.animation = "wallwalkup"
					$Anim.flip_h = false
				if axis.x == 0:
					$Anim.animation = "idlewallup"
				if axis.x < 0:
					$Anim.animation = "wallwalkup"
					$Anim.flip_h = true
					
			elif currentdir == dirs.UP:
				$Anim.offset.y = -2
				$Collision.scale.y = 0.5
				wall_cast(Vector2(0,safedist),Vector2(0,objectdist))
				if axis.x > 0:
					$Anim.animation = "wallwalkdown"
					$Anim.flip_h = false
				if axis.x == 0:
					$Anim.animation = "idlewalldown"
				if axis.x < 0:
					$Anim.animation = "wallwalkdown"
					$Anim.flip_h = true
					
					
			elif currentdir == dirs.RIGHT:
				$Collision.scale.x = 0.5
				wall_cast(Vector2(0,-safedist),Vector2(0,-objectdist))
				if axis.y > 0:
					$Anim.animation = "wallwalkleft"
					$Anim.flip_h = false
				if axis.y == 0:
					$Anim.animation = "idlewallright"
				if axis.y < 0:
					$Anim.animation = "wallwalkright"
					$Anim.flip_h = false
					
			elif currentdir == dirs.LEFT:
				$Anim.offset.x = 1
				$Collision.scale.x = 0.5
				wall_cast(Vector2(0,-safedist),Vector2(0,-objectdist))
				if axis.y > 0:
					$Anim.animation = "wallwalkleft"
					$Anim.flip_h = true
				if axis.y == 0:
					$Anim.animation = "idlewallright"
				if axis.y < 0:
					$Anim.animation = "wallwalkright"
					$Anim.flip_h = true
			
			else:	
				if !wall_check():
					leave_wall()
												
							
		ROLL:
			if !rollinit:
				moveaxis = get_input_axis()
				$RollTimer.start(ROLL_DURATION)
				rollinit = true
			
			#Cancel a roll if it is atleast 2/3 done and the player inputs the opposite direction or presses space again
			if $RollTimer.time_left <= (2*ROLL_DURATION)/3:
				if get_input_axis() == -moveaxis or Input.is_action_just_pressed("ui_select"):
					_on_RollTimer_timeout(true)
				
			#Adjust rolls if the player holds relative left or right
			if get_input_axis() != -moveaxis and get_input_axis() != Vector2(0,0):
				if (get_input_axis().angle() < moveaxis.angle() + deg2rad(90)) and get_input_axis().angle() > moveaxis.angle():
					move_and_slide(moveaxis.rotated(deg2rad(90))*ROLL_ADJUST_SPEED)
				if (get_input_axis().angle() > moveaxis.angle() + deg2rad(-90)) and get_input_axis().angle() < moveaxis.angle():
					move_and_slide(moveaxis.rotated(deg2rad(-90))*ROLL_ADJUST_SPEED)
			#The left direction doesn't want to cooperate for some reason so this code makes it actually work	
			if currentdir == dirs.LEFT and get_input_axis() != -moveaxis and get_input_axis() != Vector2(0,0):
				if get_input_axis().angle() < moveaxis.angle():
					move_and_slide(moveaxis.rotated(deg2rad(90))*ROLL_ADJUST_SPEED)
				if (get_input_axis().angle() > moveaxis.angle() + deg2rad(-90)) and get_input_axis().angle() < moveaxis.angle():
					move_and_slide(moveaxis.rotated(deg2rad(-90))*ROLL_ADJUST_SPEED)

	
			$Anim.animation = rollsprites[currentdir]
			move(ROLL_SPEED,ACCEL,FRICTION,delta,moveaxis)
			
			if $WallCheck.is_colliding():
				$Anim.animation = bonksprites[currentdir]
				$Timers/BonkTimer.start(0.55)
				sound.player_sound(load("res://sounds/player/bonk.wav"),0.1)
				currentstate = BONK
			
			
			
		SKID:
			if !skidinit:
				sound.player_sound(load("res://sounds/player/skid2.wav"),0.1)
				$Timers/SkidTimer.start(SKID_DURATION)
				$Anim.animation = skidsprites[currentdir]
				skidinit = true
				$GroundParticles/SkidParticles.visible = true
			move(ROLL_SPEED*($Timers/SkidTimer.time_left/SKID_DURATION),ACCEL,FRICTION,delta,moveaxis)
			
			
			
		BONK:
			pass
			
			
		THROW:
			if !throwinit:
				throwinit = true
				$Anim.animation = throwsprites[currentdir]
				#Wait for the throwing animation
				var t = Timer.new()
				t.set_wait_time(0.4)
				t.set_one_shot(true)
				self.add_child(t)
				t.start()
				yield(t, "timeout")
				#Throw the item
				if helditem == items.FLASK:
					var newflask = flask.instance()
					newflask.angle = -stored_velocity.angle()
					newflask.position = position + (stored_velocity*5)
					newflask.position.y -= 5
					newflask.speed = newflask.yeetspeed
					get_parent().add_child(newflask)
					$helditem/Sprite.visible = false
				
				yield($Anim,"animation_finished")
				holding = false
				helditem = null
				throwinit = false
				currentstate = WALKING
				
				
		FALLING:
			position.y -= 5
			
						
	update_shadow($Anim.animation)
	#$Silhouette.animation = $Anim.animation
	#$Silhouette.frame = $Anim.frame
	#$Silhouette.flip_h = $Anim.flip_h


func _on_WallTimer_timeout():
	var input = get_input_axis()
	if wall_check() and (input.x != 0 or input.y != 0) and !holding:
		currentstate = WALL
		sound.player_sound(load("res://sounds/player/attach.wav"))
	wallinit = false


func wall_check(): #Check if the player is on a TILEMAP wall
	var bodies = $WallCheck.get_collider()
	if bodies != null:
		if bodies.get_class() == "TileMap":
			return true
	return false
	
	
func leave_wall():
	if ($WallCast1.get_collider() == null and $WallCast2.get_collider() == null):
		$WallCast1.enabled = false
		$WallCast2.enabled = false
		wallinit = false
		currentdir = dirs.DOWN
		sound.player_sound(load("res://sounds/player/detach.wav"))
		print("leaving wall")
		currentstate = WALKING
	
	
func _input(_event):
	if currentstate == WALKING or currentstate == WALL:
		if Input.is_action_just_pressed("ui_select"):
			var objects = $Interact.get_collider()
			if objects != null:
				if objects.has_method("use"):
					objects.use(self)
			else:
				if currentstate == WALKING:
					if !holding:
						if get_input_axis() != Vector2.ZERO and !rollinit and rollenabled:
							stats.take_damage(2)
							currentstate = ROLL
					else:
						currentstate = THROW
	
	
func update_shadow(animname):
	$Shadow.flip_h = $Anim.flip_h
	$helditem/Sprite.flip_h = $Anim.flip_h
	
	if animname == "idledown" or animname == "idleup" or animname == "walkup" or animname == "walkdown":
		$Shadow.animation = "idledown"
		$Shadow.flip_h = false
	if animname == "idleright" or animname == "walkright":
		$Shadow.animation = "idleright"


func _on_RollTimer_timeout(skid=false): #Set a cooldown for rolling and resume walking
	$RollCooldown.start(ROLL_COOLDOWN)
	if skid:
		currentstate = SKID
	else:
		if currentstate != BONK:
			currentstate = WALKING


func _on_RollCooldown_timeout():
	rollinit = false


func get_pos():
	return position
	
	
func die():
# warning-ignore:return_value_discarded
	stats.health = stats.max_health



func _on_RegenTimer_timeout():
	stats.heal(1)
	
func reset_regen():
	$Timers/RegenTimer.start(1.5)


func _on_SkidTimer_timeout():
	$GroundParticles/SkidParticles.visible = false
	currentstate = WALKING
	skidinit = false


func _on_BonkTimer_timeout():
	currentstate = WALKING


func give_item(type):
	helditem = type
	holding = true
	$helditem/Sprite.visible = true

func slow_down():
	speed = MAX_SPEED/1.5
	
func normal_speed():
	speed = MAX_SPEED
	
func toggle_roll(state=true):
	rollenabled = state
