#include "blitz/protocol/packets/ChatPacket.h"

#include "blitz/misc/Log.h"
#include "blitz/misc/Maths.h"
#include <map>
#include <sstream>

namespace blitz {
namespace protocol {

const static std::map<std::string, Vec4f> COLORS = {
	{"aqua", {0, 1, 1, 1}},
	{"blue", {0, 0, 1, 1}},
	{"green", {0, 1, 0, 1}},
	{"purple", {1, 0, 1, 1}},
	{"red", {1, 0, 0, 1}},
	{"white", {1, 1, 1, 1}},
	{"yellow", {1, 1, 0, 1}},
};

DataBuffer ChatPacket::Serialize(bool packetID) const {
	DataBuffer data;

	WritePacketID(data, packetID);

	data << static_cast<std::uint64_t>(m_Message.size());
	for (const auto& part : m_Message) {
		data << part.color << part.text;
	}
	return data;
}

void ChatPacket::Deserialize(DataBuffer& data) {
	std::uint64_t partsNumber;
	data >> partsNumber;

	m_Message.resize(partsNumber);

	for (std::size_t i = 0; i < partsNumber; ++i) {
		data >> m_Message[i].color >> m_Message[i].text;
	}
}

std::string ChatPacket::GetTextColor(Vec3uc color) {
	std::stringstream ss;
	ss << std::hex << "\\u";
	if (color.r <= 0xF)
		ss << 0;
	ss << color.r;
	if (color.g <= 0xF)
		ss << 0;
	ss << color.g;
	if (color.b <= 0xF)
		ss << 0;
	ss << color.b;
	return ss.str();
}

ColoredText ChatPacket::ColorizeText(const std::string& text) {
	ColoredText parts;

	Vec4f currentColor{1, 1, 1, 1};
	std::size_t cursor = 0;
	while (true) {
		std::size_t pos = text.find('\\', cursor);

		if (pos != std::string::npos) {
			switch (text[pos + 1]) {
				case 'u': {
					if (text.size() - pos <= 7)
						break;

					std::string textPart = text.substr(cursor, pos - cursor);
					if (!textPart.empty())
						parts.push_back({currentColor, textPart});

					cursor = pos + 2;
					std::string red = text.substr(cursor, 2);
					std::string green = text.substr(cursor + 2, 2);
					std::string blue = text.substr(cursor + 4, 2);
					try {
						currentColor = {static_cast<float>(std::stoul(red, nullptr, 16)) / 255.0f,
							static_cast<float>(std::stoul(green, nullptr, 16)) / 255.0f,
							static_cast<float>(std::stoul(blue, nullptr, 16)) / 255.0f, 1.0f};
						cursor += 6;
					} catch (std::exception& e) {
						utils::LOG("warning ! wrong color providen !");
					}
					break;
				}

				case 'r': {
					std::string textPart = text.substr(cursor, pos - cursor);
					if (!textPart.empty())
						parts.push_back({currentColor, textPart});
					currentColor = {1, 1, 1, 1};
					cursor = pos + 2;
					break;
				}

				case 'c': {
					std::size_t endPos = text.find(';', cursor);

					if (endPos == std::string::npos && text.size() - endPos < 0)
						break;

					std::string textPart = text.substr(cursor, pos - cursor);
					if (!textPart.empty())
						parts.push_back({currentColor, textPart});

					cursor = pos + 2;
					std::string stringColor = text.substr(cursor, endPos - cursor);

					auto it = COLORS.find(stringColor);

					if (it != COLORS.end()) {
						currentColor = it->second;
						cursor = endPos + 1;
					}

					break;
				}

				default: {
					cursor = pos + 2;
					break;
				}
			}
		} else {
			parts.push_back({currentColor, text.substr(cursor, text.size() - cursor)});
			break;
		}
	}
	return parts;
}

std::string ChatPacket::GetColoredTextString(const ColoredText& text) {
	std::string result;

	for (auto& part : text) {
		result += GetTextColor({static_cast<std::uint8_t>(part.color.x * 255), static_cast<std::uint8_t>(part.color.y * 255),
								   static_cast<std::uint8_t>(part.color.z * 255)})
					  .c_str();
		result += part.text.c_str();
	}

	return result;
}

}  // namespace protocol
}  // namespace blitz
