extends CharacterBody3D

var gravity : float = ProjectSettings.get_setting("physics/3d/default_gravity")

var grabbable : bool = false
var shootable : bool = true
var weakpoint : bool = false
var playerallied : bool = false
var rootnode : bool = true
var waitingfor : int = 0

var perception_refreshinterval : float = 0.6
var perception_radius : float = 32
var perception_giveuptime : float = 6

var path_refreshinterval : float = 1.5

@export var hp : float = 8
@export var physics_push_force : float = 1.5
var lunge_startdistance : float = 6
var lunge_pausetime : float = 0.1
var lunge_traveldistance : float = 12
var lunge_travelheight : float = 0.3
var lunge_velocity : float = 18
var lunge_upwardvelocity : float = 4
var lunge_damage : float = 8
var lunge_recovery : float = 0.4

var currentlunge_angle : Vector3
var currentlunge_startpos : Vector3
var currentlunge_apex : float
var currentlunge_lethal : bool = true

var state : String = "idle"

var movement_speed: float = 8
var movement_delta: float
var path_point_margin: float = 1

var current_path_index: int = 0
var current_path_point: Vector3
var current_path: PackedVector3Array
@onready var default_3d_map_rid: RID = get_world_3d().get_navigation_map()
var startpos : Vector3
var forth : bool = false

@export var min_player_distance : float = 2.0
@export var min_ally_distance : float = 6.0
var runtarget : Vector3
@export var player : Node

const sprite_front1 = preload("res://sprites/enemies/chompyboy_front1.png")
const sprite_left1 = preload("res://sprites/enemies/chompyboy_left1.png")
const sprite_right1 = preload("res://sprites/enemies/chompyboy_right1.png")
const sprite_back1 = preload("res://sprites/enemies/chompyboy_back1.png")

# Called when the node enters the scene tree for the first time.
func _ready():	
	rotation_degrees.y += 180
	
	player.enemies.append(self)
	
	$perception_timer.start(perception_refreshinterval)



func _physics_process(delta):
	sprite_angle()
	ai_process()

	if state == "chasing" and player.global_transform.origin.distance_to(runtarget) > 4:
		runtarget = player.global_transform.origin
	
	if current_path.is_empty() and NavigationServer3D.map_is_active(default_3d_map_rid):
		if state == "chasing" and global_transform.origin.distance_to(runtarget) > 4:
			set_movement_target(runtarget)
			$chase_timer.start(path_refreshinterval)
	
	move_and_slide()
	
	if velocity.y > 3:
		print("hey! stop that! current state "+state+" current velocity "+str(velocity.y)+"current motion mode "+str(get_motion_mode()))
		velocity.y = 3
	
	for i in get_slide_collision_count():
		var slaminto = get_slide_collision(i)
		if slaminto.get_collider() is RigidBody3D:
			slaminto.get_collider().apply_central_impulse(-slaminto.get_normal() * physics_push_force)
		elif slaminto.get_collider() is CharacterBody3D:
			slaminto.get_collider().velocity += (-slaminto.get_normal() * movement_speed)
			if (state == "lunge_rise" or state == "lunge_descend") and slaminto.get_collider().get("playerallied") == true and currentlunge_lethal == true and slaminto.get_collider().get("hp") > 0:
				slaminto.get_collider().deal_damage(lunge_damage)
				currentlunge_lethal = false
	
	if !current_path.is_empty():
		if global_transform.origin.distance_to(player.global_transform.origin) <= min_player_distance:
				clearpath()
				$chase_timer.start(path_refreshinterval)
		
		if global_transform.origin.distance_to(current_path_point) <= path_point_margin:
			current_path_index += 1
			if current_path_index >= current_path.size():
				clearpath()
				$chase_timer.start(path_refreshinterval)


	if state == "chasing" and is_on_floor() and !current_path.is_empty() and hp > 0:
		if len(current_path) > 0:
			current_path_point = current_path[current_path_index]
			
			var direction = global_transform.origin.direction_to(current_path_point)
			var acceleration = 8.0

			velocity.x = lerp(velocity.x, direction.x * movement_speed, acceleration * delta)
			velocity.z = lerp(velocity.z, direction.z * movement_speed, acceleration * delta)
			
			# global_transform = global_transform.interpolate_with(global_transform.looking_at(Vector3(current_path_point.x,global_position.y,current_path_point.z)), 0.4)
			global_transform = global_transform.looking_at(Vector3(current_path_point.x,global_position.y,current_path_point.z))
	
	elif state == "lunge_start":
		currentlunge_startpos = global_transform.origin
		currentlunge_angle = global_transform.origin.direction_to(player.global_transform.origin)
		currentlunge_apex = player.global_transform.origin.y + lunge_travelheight
		global_transform = global_transform.looking_at(Vector3(player.global_transform.origin.x,global_position.y,player.global_transform.origin.z))
		state = "lunge_rise"
		motion_mode = 1
		
	elif state == "lunge_rise":
		var acceleration = 8.0
		velocity.x = lerp(velocity.x, currentlunge_angle.x * lunge_velocity, acceleration * delta)
		velocity.z = lerp(velocity.z, currentlunge_angle.z * lunge_velocity, acceleration * delta)
		
		if currentlunge_startpos.distance_to(global_transform.origin) >= lunge_traveldistance:
			state = "lunge_startrecovery"
		elif global_transform.origin.y < currentlunge_apex:
			velocity.y = lerp(velocity.y, lunge_upwardvelocity, acceleration * delta)
		else:
			motion_mode = 0
			state = "lunge_descend"
			
		if get_real_velocity().x == 0 and get_real_velocity().z == 0 and $ai_timer.is_stopped():
			$ai_timer.start(1)
	
	elif state == "lunge_descend":
		var acceleration = 8.0
		velocity.x = lerp(velocity.x, currentlunge_angle.x * lunge_velocity, acceleration * delta)
		velocity.z = lerp(velocity.z, currentlunge_angle.z * lunge_velocity, acceleration * delta)
		
		if currentlunge_startpos.distance_to(global_transform.origin) >= lunge_traveldistance:
			state = "lunge_startrecovery"
		
		if get_real_velocity().x == 0 and get_real_velocity().z == 0 and $ai_timer.is_stopped():
			$ai_timer.start(1)
	
	elif state == "lunge_recovering":
		var acceleration = 8.0
		velocity.x = lerp(velocity.x, 0.0, acceleration * delta)
		velocity.z = lerp(velocity.z, 0.0, acceleration * delta)
	
	elif current_path.is_empty() or waitingfor != 0:
		velocity.x = 0
		velocity.z = 0
	
	if hp > 0:
		var velocityclamp = 0.25
		if abs(velocity.x) + abs(velocity.z) > (velocityclamp * 2):
			# $AnimationPlayer.play("Run")
			pass
		else:
			# $AnimationPlayer.play("Idle")
			pass
	else:
		velocity = Vector3(0,0,0)
	
	if not is_on_floor():
		velocity.y -= gravity * delta

func ai_process():
	if state == "chasing" and global_transform.origin.distance_to(player.global_transform.origin) <= lunge_startdistance:
		state = "lunge_charge"
		$ai_timer.start(lunge_pausetime)
	elif state == "lunge_startrecovery":
		motion_mode = 0
		state = "lunge_recovering"
		$ai_timer.start(lunge_recovery)
	elif state == "goidle":
		$ai_timer.stop()
		clearpath()
		state = "idle"
	pass

func deal_damage(damage):
	print(str(damage)+" damage taken, "+str(hp)+" HP left")
	hp = hp - damage
	if hp <= 0:
		print("death")
		delete_me()


func set_movement_target(target_position: Vector3):
	var start_position: Vector3 = global_transform.origin

	current_path = NavigationServer3D.map_get_path(
		default_3d_map_rid,
		start_position,
		target_position,
		true
	)

	if not current_path.is_empty():
		current_path_index = 0
		current_path_point = current_path[0]

func clearpath():
	current_path = []
	current_path_index = 0
	current_path_point = global_transform.origin

'''func _on_animation_player_animation_finished(anim_name):
	if anim_name == "Idle":
		# $AnimationPlayer.play("Idle")
		pass
	if anim_name == "Run":
		# $AnimationPlayer.play("Run")
		pass
	if anim_name == "Death":
		delete_me()
		"""set_collision_layer_value(1, false)
		$Head.set_collision_layer_value(1, false)"""'''

func delete_me():
	player.enemies.erase(self)
	get_parent().remove_child(self)
	queue_free()
	
func sprite_angle():
	var playerpos = Vector2(player.global_position.x, player.global_position.z)
	var spritepos = Vector2(global_position.x, global_position.z)
	var directiontoplayer = rad_to_deg((playerpos - spritepos).angle())
	var selfangle = rad_to_deg(global_rotation.y)
	var spriteangle = fposmod((directiontoplayer + selfangle) + 90, 359)
	'''print("playerdirection "+str(directiontoplayer)+", angle "+str(selfangle)+", sprite "+str(spriteangle))'''
	
	if spriteangle < 45 and $Sprite.texture != sprite_front1:
		$Sprite.texture = sprite_front1
	elif spriteangle >= 45 and spriteangle < 135 and $Sprite.texture != sprite_right1:
		$Sprite.texture = sprite_right1
	elif spriteangle >= 135 and spriteangle < 225 and $Sprite.texture != sprite_back1:
		$Sprite.texture = sprite_back1
	elif spriteangle >= 225 and spriteangle < 315 and $Sprite.texture != sprite_left1:
		$Sprite.texture = sprite_left1
	elif spriteangle >= 315 and spriteangle < 360 and $Sprite.texture != sprite_front1:
		$Sprite.texture = sprite_front1

func _on_chase_timer_timeout():
	if player.global_transform.origin.distance_to(runtarget) > 4:
		runtarget = player.global_transform.origin
	if global_transform.origin.distance_to(runtarget) > 4:
		set_movement_target(runtarget)
		$chase_timer.start(path_refreshinterval)
	
	
func _on_ai_timer_timeout():
	$ai_timer.stop()
	if state == "lunge_charge":
		state = "lunge_start"
		currentlunge_lethal = true
	elif state == "lunge_recovering":
		state = "chasing"
	# halt lunge when stuck in same position for too long
	elif state == "lunge_rise" and get_real_velocity().x == 0 and get_real_velocity().z == 0:
		state = "lunge_startrecovery"
	elif state == "lunge_descend" and get_real_velocity().x == 0 and get_real_velocity().z == 0:
		state = "lunge_startrecovery"
	# go idle when chasing player that has been out of sight for too long
	elif state == "chasing":
		state = "goidle"
		print("player gone, giving up...")


func _on_perception_timer_timeout():
	$perception_timer.stop()
	$sightchecker.target_position = to_local(player.global_transform.origin)
	$sightchecker.force_raycast_update()
	
	if global_transform.origin.distance_to(player.global_transform.origin) <= perception_radius and $sightchecker.get_collider() == player:
		if state == "idle":
			state = "chasing"
			print("player sighted!")
		elif state == "chasing" and !$ai_timer.is_stopped():
			$ai_timer.stop()
	elif state == "chasing" and $ai_timer.is_stopped():
		$ai_timer.start(perception_giveuptime)
	$perception_timer.start(perception_refreshinterval)


func _on_hitflash_timer_timeout():
	pass # Replace with function body.
