#include "IRC.h"

#include "Utils/ChatUtils.h"
#include "Utils/StringUtils.h"
#include "Utils/TimeUtils.h"

#include "Datas/UserDatas.h"

auto IRC::sendMessage(std::string message) -> void {
	std::string rolereg = _("");
	if (Datas::User::role == Datas::User::Role::Developer)
		rolereg = _(".Developer");
	else if (Datas::User::role == Datas::User::Supporter)
		rolereg = _(".Supporter");

	bool isCommand = false;
	if (message == _("GET_OTHER_USER_COUNT") || message == _("NICK ") + Datas::User::name + rolereg)
		isCommand = true;

	std::string sendmsg = message.c_str();
	if (!isCommand)
		sendmsg += _("@FromClient");

	send(clientsocket, sendmsg.c_str(), static_cast<int>(sendmsg.size()) + 1, 0);

	if (isCommand) return;

	static std::string role;
	if (role.empty()) {
		switch (Datas::User::role) {
		case Datas::User::Role::Supporter:
			role = reinterpret_cast<const char*>(_(u8"§l§d[Supporter]§r "));
			break;
		case Datas::User::Role::Developer:
			role = reinterpret_cast<const char*>(_(u8"§l§9[Developer]§r "));
			break;
		}
	}
	std::string displaymsg = role + Datas::User::name + reinterpret_cast<const char*>(_(u8" » ")) + message;
	{
		std::string result;
		std::regex exactMentionRegex(_("(@") + Datas::User::name + _("(?![a-zA-Z0-9_]))"));
		std::sregex_iterator it(displaymsg.begin(), displaymsg.end(), exactMentionRegex);
		std::sregex_iterator end;

		size_t lastPos = 0;
		while (it != end) {
			std::smatch match = *it;
			size_t matchPos = match.position();
			std::string matchedStr = match.str();

			std::regex colorCodeRegex(reinterpret_cast<const char*>(_(u8"§[0-9a-fk-or]")));
			std::string lastCode;
			auto begin = std::sregex_iterator(displaymsg.begin(), displaymsg.begin() + matchPos, colorCodeRegex);
			auto end = std::sregex_iterator();

			for (auto it = begin; it != end; ++it) {
				lastCode = it->str();
			}

			std::string prevColor = lastCode.empty() ? reinterpret_cast<const char*>(_(u8"§r")) : lastCode;

			result += displaymsg.substr(lastPos, matchPos - lastPos);
			result += reinterpret_cast<const char*>(_(u8"§d")) + matchedStr + prevColor;

			lastPos = matchPos + matchedStr.length();
			++it;
		}

		result += displaymsg.substr(lastPos);
		displaymsg = result;
	}
	{
		std::string result;
		std::regex exactMentionRegex(_("(@everyone(?![a-zA-Z0-9_]))"));
		std::sregex_iterator it(displaymsg.begin(), displaymsg.end(), exactMentionRegex);
		std::sregex_iterator end;

		size_t lastPos = 0;
		while (it != end) {
			std::smatch match = *it;
			size_t matchPos = match.position();
			std::string matchedStr = match.str();

			std::regex colorCodeRegex(reinterpret_cast<const char*>(_(u8"§[0-9a-fk-or]")));
			std::string lastCode;
			auto begin = std::sregex_iterator(displaymsg.begin(), displaymsg.begin() + matchPos, colorCodeRegex);
			auto end = std::sregex_iterator();

			for (auto it = begin; it != end; ++it) {
				lastCode = it->str();
			}

			std::string prevColor = lastCode.empty() ? reinterpret_cast<const char*>(_(u8"§r")) : lastCode;

			result += displaymsg.substr(lastPos, matchPos - lastPos);
			result += reinterpret_cast<const char*>(_(u8"§e")) + matchedStr + prevColor;

			lastPos = matchPos + matchedStr.length();
			++it;
		}

		result += displaymsg.substr(lastPos);
		displaymsg = result;
	}

	Utils::Chat::display(displaymsg, Utils::Chat::Type::NORMAL, Utils::Chat::PrefixType::IRC);
}

auto IRC::onClient2() -> void {
	if (!enabled) {
		otherusercount = false;

		if (Client::joinirc) {
			enabled = true;
			onEnabled();
			Client::joinirc = false;
			return;
		}

		return;
	}

	if (connected) {
		{
			char buffer[512];
			fd_set readset;
			FD_ZERO(&readset);
			FD_SET(clientsocket, &readset);
			timeval timeout;
			timeout.tv_sec = static_cast<long>(0.0001);
			timeout.tv_usec = static_cast<long>(0.0001);

			if (select(static_cast<int>(clientsocket) + 1, &readset, 0, 0, &timeout) > 0) {
				if (FD_ISSET(clientsocket, &readset)) {
					int len = recv(clientsocket, buffer, 512, 0);
					if (len > 0) {
						buffer[len] = '\0';
						std::string msg(buffer);
						bool usercount = msg.substr(1, std::string(_(" other")).size()) == _(" other");
						if (usercount) msg = reinterpret_cast<const char*>(_(u8"§c§l")) + msg;
						bool issystemmsg = msg.find(_("'")) == std::string::npos;

						msg = std::regex_replace(msg, std::regex(_("&")), reinterpret_cast<const char*>(_(u8"§")));
						msg = std::regex_replace(msg, std::regex(_("'")), reinterpret_cast<const char*>(_(u8"»")));

						std::string displaymsg = reinterpret_cast<const char*>(_(u8"§7")) + msg;
						{
							std::string result;
							std::regex exactMentionRegex(_("(@") + Datas::User::name + _("(?![a-zA-Z0-9_]))"));
							std::sregex_iterator it(displaymsg.begin(), displaymsg.end(), exactMentionRegex);
							std::sregex_iterator end;

							size_t lastPos = 0;
							while (it != end) {
								std::smatch match = *it;
								size_t matchPos = match.position();
								std::string matchedStr = match.str();

								std::regex colorCodeRegex(reinterpret_cast<const char*>(_(u8"§[0-9a-fk-or]")));
								std::string lastCode;
								auto begin = std::sregex_iterator(displaymsg.begin(), displaymsg.begin() + matchPos, colorCodeRegex);
								auto end = std::sregex_iterator();

								for (auto it = begin; it != end; ++it) {
									lastCode = it->str();
								}

								std::string prevColor = lastCode.empty() ? reinterpret_cast<const char*>(_(u8"§r")) : lastCode;

								result += displaymsg.substr(lastPos, matchPos - lastPos);
								result += reinterpret_cast<const char*>(_(u8"§d")) + matchedStr + prevColor;

								lastPos = matchPos + matchedStr.length();
								++it;
							}

							result += displaymsg.substr(lastPos);
							displaymsg = result;
						}
						{
							std::string result;
							std::regex exactMentionRegex(_("(@everyone(?![a-zA-Z0-9_]))"));
							std::sregex_iterator it(displaymsg.begin(), displaymsg.end(), exactMentionRegex);
							std::sregex_iterator end;

							size_t lastPos = 0;
							while (it != end) {
								std::smatch match = *it;
								size_t matchPos = match.position();
								std::string matchedStr = match.str();

								std::regex colorCodeRegex(reinterpret_cast<const char*>(_(u8"§[0-9a-fk-or]")));
								std::string lastCode;
								auto begin = std::sregex_iterator(displaymsg.begin(), displaymsg.begin() + matchPos, colorCodeRegex);
								auto end = std::sregex_iterator();

								for (auto it = begin; it != end; ++it) {
									lastCode = it->str();
								}

								std::string prevColor = lastCode.empty() ? reinterpret_cast<const char*>(_(u8"§r")) : lastCode;

								result += displaymsg.substr(lastPos, matchPos - lastPos);
								result += reinterpret_cast<const char*>(_(u8"§e")) + matchedStr + prevColor;

								lastPos = matchPos + matchedStr.length();
								++it;
							}

							result += displaymsg.substr(lastPos);
							displaymsg = result;
						}

						Utils::Chat::display(displaymsg, Utils::Chat::Type::NORMAL, Utils::Chat::PrefixType::IRC);
						if (!issystemmsg && Client::screenName == _("pause_screen"))
							Client::notifications.push_back({ _("IRC"), _("New message."), 2.5f });
					}
				}
			}
		}


		long long now = Utils::Time::getMs();
		static long long start = now;
		static int failed = 0;
		if (now - start >= 1000.f) {
			fd_set readset;
			FD_ZERO(&readset);
			FD_SET(clientsocket, &readset);
			timeval timeout;
			timeout.tv_sec = static_cast<long>(0.0001);
			timeout.tv_usec = static_cast<long>(0.0001);

			if (select(static_cast<int>(clientsocket) + 1, &readset, 0, 0, &timeout) > 0) {
				if (FD_ISSET(clientsocket, &readset)) {
					char buf[1];
					int data = recv(clientsocket, buf, sizeof(buf), MSG_PEEK);

					long long now = Utils::Time::getMs();
					static long long start = now;
					static bool queue = false;
					if (data == 0 || data == -1) {
						failed++;
					}
					else {
						failed = 0;
					}
				}
			}

			if (failed > 5) {
				Utils::Chat::display(_("Disconnected from IRC Chat."), Utils::Chat::Type::RED, Utils::Chat::PrefixType::IRC);

				enabled = false;
				connected = false;
				WSACleanup();

			}

			start = now;
		}

		if (!otherusercount) {
			sendMessage(_("GET_OTHER_USER_COUNT"));
			otherusercount = true;
		}

		if (Client::kickirc) {
			enabled = false;
			onDisabled();
			Client::kickirc = false;
			return;
		}
	}
}

auto IRC::onEnabled() -> void {
	if (connected) return;

	std::thread([&] {
		while (
			Client::screenName != _("hud_screen") &&
			Client::screenName != _("pause_screen") &&
			Client::screenName != _("chat_screen")
			) {
			Sleep(0);
		}

		WSAData wsaData;
		if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
			Utils::Chat::display_(_("Failed to start connection."), Utils::Chat::Type::RED, Utils::Chat::PrefixType::IRC);
			Utils::Chat::display(_("Connection failded."), Utils::Chat::Type::RED, Utils::Chat::PrefixType::IRC);

			enabled = false;
			return;
		}

		clientsocket = socket(AF_INET, SOCK_STREAM, 0);
		if (clientsocket == INVALID_SOCKET) {
			Utils::Chat::display_(_("Failed to create connection."), Utils::Chat::Type::RED, Utils::Chat::PrefixType::IRC);
			Utils::Chat::display(_("Connection failded."), Utils::Chat::Type::RED, Utils::Chat::PrefixType::IRC);
			WSACleanup();

			enabled = false;
			return;
		}

		sockaddr_in hint;
		hint.sin_family = AF_INET;
		hint.sin_port = htons(6667);
		hint.sin_addr.s_addr = inet_addr(_("192.168.1.80"));

		Sleep(100);
		Utils::Chat::display(_("Connecting..."), Utils::Chat::Type::GREEN, Utils::Chat::PrefixType::IRC);

		if (connect(clientsocket, (sockaddr*)&hint, sizeof(hint)) == SOCKET_ERROR) {
			struct addrinfo hints, * res;
			memset(&hints, 0, sizeof(hints));
			hints.ai_family = AF_INET;
			hints.ai_socktype = SOCK_STREAM;

			if (getaddrinfo(_("www.valkyrieclient.xyz"), _("6667"), &hints, &res) != 0) {
				if (getaddrinfo(_("valkyrieclient.ddns.net"), _("6667"), &hints, &res) != 0) {
					Utils::Chat::display_(_("Failed to get connection infomation."), Utils::Chat::Type::RED, Utils::Chat::PrefixType::IRC);
					Utils::Chat::display(_("Connection failded."), Utils::Chat::Type::RED, Utils::Chat::PrefixType::IRC);
					WSACleanup();

					enabled = false;
					return;
				}
			}

			hint.sin_addr = ((sockaddr_in*)res->ai_addr)->sin_addr;

			if (connect(clientsocket, (sockaddr*)&hint, sizeof(hint)) == SOCKET_ERROR) {
				Utils::Chat::display_(_("Failed to connect connection."), Utils::Chat::Type::RED, Utils::Chat::PrefixType::IRC);
				Utils::Chat::display(_("Connection failded."), Utils::Chat::Type::RED, Utils::Chat::PrefixType::IRC);

				closesocket(clientsocket);
				WSACleanup();

				enabled = false;
				return;
			}
		}

		std::string rolereg = _("");
		if (Datas::User::role == Datas::User::Role::Developer)
			rolereg = _(".Developer");
		else if (Datas::User::role == Datas::User::Supporter)
			rolereg = _(".Supporter");

		sendMessage(_("NICK ") + Datas::User::name + rolereg);

		connected = true;
		}).detach();
}

auto IRC::onDisabled() -> void {
	if (connected) {
		std::string rolereg = _("");
		std::string color = _("");
		if (Datas::User::role == Datas::User::Role::Developer) {
			color = reinterpret_cast<const char*>(_(u8"§9"));
			rolereg = reinterpret_cast<const char*>(_(u8"§9Developer"));
		}
		else if (Datas::User::role == Datas::User::Supporter) {
			color = reinterpret_cast<const char*>(_(u8"§d"));
			rolereg = reinterpret_cast<const char*>(_(u8"§dSupporter"));
		}

		Utils::Chat::display((!rolereg.empty() ? (
			reinterpret_cast<const char*>(_(u8"§l")) + color + _("[") +
			rolereg +
			reinterpret_cast<const char*>(_(u8"]§r "))) : _("")) + Datas::User::name + _(" has left from chat."), Utils::Chat::Type::NORMAL, Utils::Chat::PrefixType::IRC);

		closesocket(clientsocket);
		WSACleanup();

		Utils::Chat::display(_("Disconnected from IRC Chat."), Utils::Chat::Type::RED, Utils::Chat::PrefixType::IRC);
		connected = false;
	}
}

auto IRC::onPacketSendAfter(Packet* packet, bool* cancel) -> void {
	if (!connected) return;

	if (packet->getID() == PacketID::Text) {
		auto textpacket = reinterpret_cast<TextPacket*>(packet);
		if (textpacket->getName().empty()) return;

		std::string message = textpacket->getMessage();
		if (toggle ? message.at(0) != '#' : message.at(0) == '#') {
			*cancel = true;

			sendMessage(toggle ? message : message.substr(1));
		}
		else if (toggle && message.at(0) == '#') {
			message = message.substr(1);
		}
		textpacket->setMessage(message);
	}
}
