#include "World.h"

#include "FirstPersonPlayer.h"
#include "NetworkInterface.h"
#include "Player.h"
#include <godot_cpp/classes/engine.hpp>
#include <godot_cpp/classes/multiplayer_api.hpp>
#include <godot_cpp/classes/packed_scene.hpp>
#include <godot_cpp/classes/resource_loader.hpp>
#include <godot_cpp/variant/utility_functions.hpp>


using namespace godot;

namespace blitz {

static const char FirstPersonPlayerScenePath[] = "res://Scenes/Characters/first_person_player.tscn";
static const char PlayerScenePath[] = "res://Scenes/Characters/player.tscn";

void World::_bind_methods() {}

void World::_ready() {
	if (Engine::get_singleton()->is_editor_hint())
		return;

	m_Players = find_child("Players");
	DEV_ASSERT(m_Players);

	auto* lobby = get_parent()->find_child("Lobby");
	DEV_ASSERT(lobby);

	m_NetworkInterface = Object::cast_to<NetworkInterface>(lobby->find_child("NetworkInterface"));
	DEV_ASSERT(m_NetworkInterface);

	m_NetworkInterface->connect("AddPlayer", callable_mp(this, &World::AddPlayer));
	m_NetworkInterface->connect("RemovePlayer", callable_mp(this, &World::RemovePlayer));
	m_NetworkInterface->connect("SetPlayerPositionAndRotation", callable_mp(this, &World::SetPlayerPositionAndRotation));
}


World::World() {}

World::~World() {}

void World::_process(float delta) {
#if DEBUG_ENABLED
	if (Engine::get_singleton()->is_editor_hint())
		return;
#endif
	if (get_multiplayer()->is_server()) {
		for (int i = 0; i < m_Players->get_child_count(); i++) {
			Player* player = Object::cast_to<Player>(m_Players->get_child(i));
			DEV_ASSERT(player);
			m_NetworkInterface->rpc(
				"SetPlayerPositionAndRotation", player->m_PeerId, player->get_position(), player->GetCameraRotation());
		}
	} else {
		Player* player = GetPlayerById(get_multiplayer()->get_unique_id());
		if (player)
			m_NetworkInterface->rpc("SetPlayerPositionAndRotation", get_multiplayer()->get_unique_id(), player->get_position(),
				player->GetCameraRotation());
	}
}

Player* World::GetPlayerById(uint64_t a_PlayerId) {
	String stringId = UtilityFunctions::var_to_str(a_PlayerId);
	for (int i = 0; i < m_Players->get_child_count(); i++) {
		Node* player = m_Players->get_child(i);
		if (player->get_name() == stringId) {
			return Object::cast_to<Player>(player);
		}
	}
	return nullptr;
}

void World::AddPlayer(int32_t a_PlayerId, String a_PlayerName) {
	UtilityFunctions::print("New Player with id : ", a_PlayerId);
	if (a_PlayerId == get_multiplayer()->get_unique_id()) {
		Ref<PackedScene> serverScene = ResourceLoader::get_singleton()->load(FirstPersonPlayerScenePath);
		FirstPersonPlayer* player = Object::cast_to<FirstPersonPlayer>(serverScene->instantiate());
		player->set_name(UtilityFunctions::var_to_str(a_PlayerId));
		player->m_PeerId = a_PlayerId;
		m_Players->add_child(player);
	} else {
		Ref<PackedScene> serverScene = ResourceLoader::get_singleton()->load(PlayerScenePath);
		Player* player = Object::cast_to<Player>(serverScene->instantiate());
		player->set_name(UtilityFunctions::var_to_str(a_PlayerId));
		player->m_PeerId = a_PlayerId;
		m_Players->add_child(player);
	}
}

void World::RemovePlayer(int32_t a_PlayerId) {
	UtilityFunctions::print("Removing Player with id : ", a_PlayerId);
	Player* player = GetPlayerById(a_PlayerId);
	if (player) {
		player->queue_free();
	}
}

void World::SetPlayerPositionAndRotation(int64_t a_PlayerId, Vector3 a_Position, Vector3 a_Rotation) {
	if (a_PlayerId == get_multiplayer()->get_unique_id())
		return;

	Player* player = GetPlayerById(a_PlayerId);
	if (player) {
		player->set_position(a_Position);
		player->SetCameraRotation(a_Rotation);
	}
}

}  // namespace blitz