#include <blitz/protocol/PacketSerializer.h>

#include <blitz/protocol/ByteBuffer.h>
#include <blitz/protocol/PacketFactory.h>
#include <blitz/protocol/PacketVisitor.h>

#include <godot_cpp/variant/utility_functions.hpp>
#include <godot_cpp/variant/variant.hpp>

namespace blitz {
namespace protocol {
namespace PacketSerializer {

#define DeclarePacket(PacketName, ...)                                                                                                \
	void Visit(const packets::PacketName& a_Packet) override {                                                                        \
		const auto& packetData = a_Packet.m_Data;                                                                                     \
		SerializePacketData(packetData);                                                                                              \
	}                                                                                                                                 \
                                                                                                                                      \
	void SerializePacketData(const packets::PacketName::PacketDataType& a_Packet);




class Serializer : public PacketVisitor {
  private:
	ByteBuffer& m_Buffer;

  public:
	Serializer(ByteBuffer& a_Buffer) : m_Buffer(a_Buffer) {}

	void Serialize(const Packet& a_Packet) {
		m_Buffer << static_cast<PacketID>(a_Packet.GetType());
		Check(a_Packet);
	}

	DeclareAllPacket()
};

#undef DeclarePacket





#define DeclarePacket(PacketName, ...)                                                                                                \
	void Visit(const packets::PacketName& a_Packet) override {                                                                        \
		auto packetPtr = PacketFactory::CreatePacket<packets::PacketName>();                                                          \
		auto& packetData = packetPtr->m_Data;                                                                                         \
                                                                                                                                      \
		DeserializePacketData(packetData);                                                                                            \
                                                                                                                                      \
		m_Packet = std::move(packetPtr);                                                                                              \
	}                                                                                                                                 \
                                                                                                                                      \
	void DeserializePacketData(packets::PacketName::PacketDataType& a_Packet);



class Deserializer : public PacketVisitor {
  private:
	ByteBuffer& m_Buffer;
	PacketPtr m_Packet;

  public:
	Deserializer(ByteBuffer&& a_Buffer) : m_Buffer(a_Buffer) {}

	bool Deserialize(const PacketPtr& a_Packet) {
		try {
			Check(*a_Packet.get());
		} catch (ByteBuffer::ReadError& e) {
			godot::UtilityFunctions::printerr("[PacketSerializer::Deserializer] ", e.what());
			return false;
		}
		return true;
	}

	PacketPtr& GetPacket() {
		return m_Packet;
	}

	DeclareAllPacket()
};





godot::PackedByteArray Serialize(const Packet& a_Packet) {
	ByteBuffer stream;

	Serializer serializer(stream);
	serializer.Serialize(a_Packet);

	return stream.GetByteArray();
}

std::unique_ptr<Packet> Deserialize(godot::PackedByteArray& a_Data) {
	ByteBuffer stream(std::move(a_Data));

	PacketID packetId;
	stream >> packetId;

	if (packetId >= static_cast<PacketID>(PacketType::PACKET_COUNT))
		return nullptr;

	PacketType packetType = PacketType(packetId);

	// for double-dispatch
	const PacketPtr& emptyPacket = PacketFactory::CreateReadOnlyPacket(packetType);

	Deserializer deserializer(std::move(stream));

	if (deserializer.Deserialize(emptyPacket)) {
		PacketPtr packet = std::move(deserializer.GetPacket());
		return packet;
	}

	return nullptr;
}





//---------------------------------------------
// Packet serializer implementation
//----------------------------------------------





void Serializer::SerializePacketData(const data::PlayerLogin& a_Packet) {
	m_Buffer << a_Packet.m_PlayerName;
}

void Deserializer::DeserializePacketData(data::PlayerLogin& a_Packet) {
	m_Buffer >> a_Packet.m_PlayerName;
}




void Serializer::SerializePacketData(const data::UpdateHealth& a_Packet) {
	m_Buffer << a_Packet.m_NewHealth;
}

void Deserializer::DeserializePacketData(data::UpdateHealth& a_Packet) {
	m_Buffer >> a_Packet.m_NewHealth;
}




void Serializer::SerializePacketData(const data::LoggingSuccess& a_Packet) {
	m_Buffer << a_Packet.m_PlayerId;
}

void Deserializer::DeserializePacketData(data::LoggingSuccess& a_Packet) {
	m_Buffer >> a_Packet.m_PlayerId;
}




void Serializer::SerializePacketData(const data::PlayerDeath& a_Packet) {}

void Deserializer::DeserializePacketData(data::PlayerDeath& a_Packet) {}




void Serializer::SerializePacketData(const data::PlayerJoin& a_Packet) {
	m_Buffer << a_Packet.m_Player;
}

void Deserializer::DeserializePacketData(data::PlayerJoin& a_Packet) {
	m_Buffer >> a_Packet.m_Player;
}




void Serializer::SerializePacketData(const data::PlayerLeave& a_Packet) {
	m_Buffer << a_Packet.m_PlayerId;
}

void Deserializer::DeserializePacketData(data::PlayerLeave& a_Packet) {
	m_Buffer >> a_Packet.m_PlayerId;
}




void Serializer::SerializePacketData(const data::PlayerList& a_Packet) {
	m_Buffer << a_Packet.m_Players;
}

void Deserializer::DeserializePacketData(data::PlayerList& a_Packet) {
	m_Buffer >> a_Packet.m_Players;
}




void Serializer::SerializePacketData(const data::PlayerStats& a_Packet) {}

void Deserializer::DeserializePacketData(data::PlayerStats& a_Packet) {}




void Serializer::SerializePacketData(const data::ServerConfig& a_Packet) {}

void Deserializer::DeserializePacketData(data::ServerConfig& a_Packet) {}




void Serializer::SerializePacketData(const data::ServerTps& a_Packet) {}

void Deserializer::DeserializePacketData(data::ServerTps& a_Packet) {}




void Serializer::SerializePacketData(const data::UpdateGameState& a_Packet) {}

void Deserializer::DeserializePacketData(data::UpdateGameState& a_Packet) {}




void Serializer::SerializePacketData(const data::KeepAlive& a_Packet) {
	m_Buffer << a_Packet.m_KeepAliveId;
}

void Deserializer::DeserializePacketData(data::KeepAlive& a_Packet) {
	m_Buffer >> a_Packet.m_KeepAliveId;
}




void Serializer::SerializePacketData(const data::Disconnect& a_Packet) {}

void Deserializer::DeserializePacketData(data::Disconnect& a_Packet) {}




void Serializer::SerializePacketData(const data::ChatMessage& a_Packet) {
	m_Buffer << a_Packet.m_Text;
}

void Deserializer::DeserializePacketData(data::ChatMessage& a_Packet) {
	m_Buffer >> a_Packet.m_Text;
}




void Serializer::SerializePacketData(const data::PlayerPositionAndRotation& a_Packet) {
	m_Buffer << a_Packet.m_Player << a_Packet.m_Position << a_Packet.m_Rotation << a_Packet.m_Velocity;
}

void Deserializer::DeserializePacketData(data::PlayerPositionAndRotation& a_Packet) {
	m_Buffer >> a_Packet.m_Player >> a_Packet.m_Position >> a_Packet.m_Rotation >> a_Packet.m_Velocity;
}




void Serializer::SerializePacketData(const data::PlayerShoot& a_Packet) {}

void Deserializer::DeserializePacketData(data::PlayerShoot& a_Packet) {}





}  // namespace PacketSerializer
}  // namespace protocol
}  // namespace blitz
