extends Node3D
class_name ServerNode

var player_node: PlayerNode

# TODO: Shoould not be defined here
const DEFAULT_WEAPON := "ak"
var first_slot_weapon: WeaponController

@export var jumping := false
@export var input_direction: Vector2
@export var owner_id: int = 0

@onready var camera_mount: CameraMount = $SharedNode/CameraMount
@onready var shared_node: CharacterBody3D = $SharedNode
@onready var bullet_starting_point: Node3D = $SharedNode/CameraMount/BulletStartingPoint
@onready var map_controller: MapController


func _ready() -> void:
	shared_node.set_collision_layer_value(3, true)
	shared_node.set_collision_mask_value(1, true)
	shared_node.set_collision_mask_value(3, true)
	map_controller = find_parent("Map")
	camera_mount.active = true
	_load_weapon()
	#map_controller.spawn_player_model(shared_node, owner_id)
	# Load the default weapon and set the current attack properties


func _load_weapon() -> void:
	var path_tmpl := "res://scenes/weapon/guns/%s/with_hands.tscn"
	var path := path_tmpl % DEFAULT_WEAPON
	var scene: PackedScene = ResourceLoader.load(path)
	var node: WeaponController = scene.instantiate()
	first_slot_weapon = node
	first_slot_weapon.make_invisible()
	first_slot_weapon.set_map_controller(map_controller)
	add_child(first_slot_weapon)


func bind_player_node() -> void:
	if multiplayer.get_unique_id() == owner_id:
		player_node = get_tree().get_root().find_child("PlayerController", true, false)


func _physics_process(delta: float) -> void:
	if not shared_node.is_on_floor():
		shared_node.velocity += shared_node.get_gravity() * delta
	if shared_node.is_on_floor() && jumping:
		shared_node.velocity.y = consts.DEFAULT_JUMP_VELOCITY
	jumping = false
	var direction := (
		(shared_node.transform.basis * Vector3(input_direction.x, 0, input_direction.y))
		. normalized()
	)
	if shared_node.is_on_floor():
		if direction:
			#first_view_legs_anim.play("Run Forward")
			shared_node.velocity.x = direction.x * consts.DEFAULT_CHARACTER_SPEED
			shared_node.velocity.z = direction.z * consts.DEFAULT_CHARACTER_SPEED
		else:
			shared_node.velocity.x = move_toward(
				shared_node.velocity.x, 0, consts.DEFAULT_CHARACTER_SPEED
			)
			shared_node.velocity.z = move_toward(
				shared_node.velocity.z, 0, consts.DEFAULT_CHARACTER_SPEED
			)
	shared_node.move_and_slide()


@rpc("authority", "call_remote", "unreliable_ordered")
func update_position(real_position: Vector3):
	if not multiplayer.is_server():
		shared_node.global_transform.origin = lerp(
			shared_node.global_transform.origin, real_position, 1.0
		)


@rpc("any_peer", "call_local", "unreliable")
func jump():
	jumping = true


@rpc("any_peer", "call_local", "unreliable")
func sync_velocity(x: float, y: float, z: float) -> void:
	if not multiplayer.is_server():
		shared_node.velocity = Vector3(x, y, z)


@rpc("any_peer", "call_local", "unreliable")
func set_input_direction(new_input_direction: Vector2):
	input_direction = new_input_direction


@rpc("any_peer", "call_local", "unreliable")
func set_rotation_x(x: float):
	camera_mount.rotation.x = x


@rpc("any_peer", "call_local", "unreliable")
func set_rotation_y(y: float):
	shared_node.rotation.y = y


func _on_reconciliation_timer_timeout() -> void:
	if multiplayer.is_server():
		#_veryfy_position_and_rotation.rpc_id(owner_id)
		#update_position.rpc(shared_node.global_transform.origin)
		$ReconciliationTimer.start()


@rpc("any_peer", "call_local", "reliable")
func _veryfy_position_and_rotation() -> void:
	player_node.verify_position()
	player_node.verify_rotation()


#@rpc("authority", "call_remote", "unreliable")
#func _sync_transorm():

@rpc("any_peer", "call_local", "reliable")
func _adjust_position(x: float, y: float, z: float) -> void:
	player_node.adjust_position(x, y, z)


@rpc("any_peer", "call_local", "reliable")
func _adjust_rotation(x: float, y: float, z: float) -> void:
	player_node.adjust_rotation(x, y, z)


@rpc("any_peer", "call_local", "reliable")
func send_position(desired_position: Vector3):
	if multiplayer.is_server():
		var real_position: Vector3 = shared_node.global_position
		var difference: Vector3 = desired_position - real_position
		if is_vector_a_lower_than_b(difference, Vector3(0.2, 0.2, 0.2)):
			shared_node.global_position = desired_position
		elif is_vector_a_lower_than_b(difference, Vector3(0.4, 0.4, 0.4)):
			var new_position: Vector3 = desired_position.lerp(real_position, 0.5)
			push_warning("player position is not valid, lerping")
			_adjust_position.rpc_id(owner_id, new_position.x, new_position.y, new_position.z)
		else:
			push_warning("player position is not valid, adjusting")
			_adjust_position.rpc_id(owner_id, real_position.x, real_position.y, real_position.z)


@rpc("any_peer", "call_local")
func send_rotation(x: float, y: float, z: float):
	if multiplayer.is_server():
		var desired_rotation: Vector3 = Vector3(x, y, z)
		if multiplayer.is_server():
			var real_rotation: Vector3 = shared_node.rotation
			var difference: Vector3 = desired_rotation - real_rotation
			if is_vector_a_lower_than_b(difference, Vector3(0.3, 0.3, 0.3)):
				shared_node.rotation = desired_rotation
			else:
				var new_rotation: Vector3 = desired_rotation.lerp(real_rotation, 0.5)
				_adjust_rotation.rpc_id(owner_id, new_rotation.x, new_rotation.y, new_rotation.z)


@rpc("any_peer", "call_local", "unreliable_ordered")
func shoot():
	first_slot_weapon.attack(bullet_starting_point)


func is_vector_a_lower_than_b(vec_a: Vector3, vec_b: Vector3) -> bool:
	return vec_a.x < vec_b.x and vec_a.y < vec_b.y and vec_a.z < vec_b.z