#include "stinc.h"
#include "stmisc.h"
#include "stconsolecommands.h"
#include "stplayer.h"
#include "stchat.h"

aVector<gzChatCommandBase *> gzChatManagerClass::m_chatCommandList;
gzChatManagerClass *gzChat = NULL;
gzChatManagerClass::gzChatManagerClass()
{
	this->RegisterEvent(EVT_GAME_LEVEL_LOADED);
	this->RegisterEvent(EVT_PLAYER_CHAT);
	this->RegisterEvent(EVT_NET_WOLPAGE);

	this->m_autoCompleteModTime = 0;
	this->m_textFilterModTime = 0;
}
void gzChatManagerClass::Delete()
{
	for (unsigned int i = 0; i < this->m_chatCommandList.Count(); i++)
		delete this->m_chatCommandList[i];
	this->m_chatCommandList.Clear();

	for (aListNode<gzAutoCompleteStruct>* list = this->m_autoComplete.GetHead(); list != NULL; list = list->GetNext())
		delete list->GetData();
	this->m_autoComplete.RemoveAll();

	for (aListNode<gzTextFilterStruct>* list = this->m_textFilter.GetHead(); list != NULL; list = list->GetNext())
		delete list->GetData();
	this->m_textFilter.RemoveAll();

	this->m_muteList.Clear();
}
void gzChatManagerClass::Level_Loaded()
{
	// Auto complete
	struct stat attrib;
	stat("AutoComplete.ini", &attrib);
	if (attrib.st_mtime != this->m_autoCompleteModTime)
	{
		this->m_autoCompleteModTime = attrib.st_mtime;

		aTextConfig accfg("AutoComplete.ini");
		if (accfg.IsLoaded())
		{
			for (aListNode<gzAutoCompleteStruct>* list = this->m_autoComplete.GetHead(); list != NULL; list = list->GetNext())
				delete list->GetData();
			this->m_autoComplete.RemoveAll();

			for (int i = 1; i <= accfg.GetSubCount("AutoComplete"); i++)
			{
				gzAutoCompleteStruct* ac = new gzAutoCompleteStruct;
				ac->input = accfg.GetItem(aString::Format("AutoComplete.[%d]", i), "");
				ac->output = accfg.GetData(aString::Format("AutoComplete.[%d]", i), "");
				this->m_autoComplete.AddTail(ac);
			}
		}
		else
			stConsole::Out("[Error] AutoComplete.ini can not be loaded or not found.\n");
	}
	
	// Text filter
	stat("TextFilter.ini", &attrib);
	if (attrib.st_mtime != this->m_textFilterModTime)
	{
		this->m_textFilterModTime = attrib.st_mtime;
		aTextConfig tfcfg("TextFilter.ini");
		if (tfcfg.IsLoaded())
		{
			for (aListNode<gzTextFilterStruct>* list = this->m_textFilter.GetHead(); list != NULL; list = list->GetNext())
				delete list->GetData();
			this->m_textFilter.RemoveAll();

			for (int i = 1; i <= tfcfg.GetRootCount(); i++)
			{
				gzTextFilterStruct *filter = new gzTextFilterStruct;
				filter->name = tfcfg.GetItem(aString::Format("[%d]", i), "");
				filter->String.Clear();
				filter->cmpType = TYPE_NONE;
				filter->enable = false;
				filter->type = MSG_NONE;
				filter->options = OPT_NONE;
				
				for (int j = 1; j <= tfcfg.GetSubCount(filter->name); j++)
				{
					aString option = tfcfg.GetItem(aString::Format("%s.[%d]", filter->name.GetString(), j), "");
					aString data = tfcfg.GetData(aString::Format("%s.[%d]", filter->name.GetString(), j), "");
					if (option == "String")
					{
						if (!data.IsEmpty())
							filter->String.Add(data);
					}
					else
					{
						aToken dataTok(data.GetString());
						for (int k = 1; k <= dataTok.numtok(' '); k++)
						{
							if (option == "Enable")
							{
								filter->enable = (tfcfg.GetData(filter->name + ".Enable", false) == false) ? false : true;
								break;
							}
							else if (option == "Type")
							{
								if (dataTok.gettok(k,' ').GetData() == "ALL")
									filter->type |= MSG_ALL;
								else if (dataTok.gettok(k,' ').GetData() == "TEAM")
									filter->type |= MSG_TEAM;
							}
							else if (option == "Compare")
							{
								if (dataTok.gettok(k,' ').GetData() == "NORM")
									filter->cmpType |= TYPE_NORM;
								else if (dataTok.gettok(k,' ').GetData() == "WILDCARD")
									filter->cmpType |= TYPE_WILDCARD;
								else if (dataTok.gettok(k,' ').GetData() == "REGEX")
									filter->cmpType |= TYPE_REGEX;
								break;
							}
							else if (option == "Option")
							{
								if (dataTok.gettok(k,' ').GetData() == "LOG")
									filter->options |= OPT_LOG;
							}
						}
					}
				}
				this->m_textFilter.AddTail(filter);
			}
		}
		else
			stConsole::Out("[Error] TextFilter.ini can not be loaded or not found.\n");
	}
}
void gzChatManagerClass::Player_Chat(gzEventPlayerChat &evt)
{
	gzPlayer *pData = gzPlayerManager::Find(evt.m_sender);
	if (pData && evt.m_sender > 0 && (evt.m_type == 0 || evt.m_type == 1))
	{
		// Mute check
		for (unsigned int i = 0; i < this->m_muteList.Count(); i++)
		{
			if (this->m_muteList[i] == pData->GetPlayerData()->PlayerName.m_Buffer)
			{
				evt.Skip();
				return;
			}
		}

		// Auto Complete
		for (aListNode<gzAutoCompleteStruct>* list = this->m_autoComplete.GetHead(); list != NULL; list = list->GetNext())
		{
			if (list->GetData()->input == evt.m_message)
			{
				evt.m_type = Team;
				evt.m_message = list->GetData()->output;
				break;
			}
		}
	}
	if (pData && evt.m_sender > 0 && evt.m_type >= 0)
	{
		// Commands
		aToken cmd(evt.m_message.GetString());
		for (unsigned int i = 0; i < gzChatManagerClass::m_chatCommandList.Count(); i++)
		{
			for (unsigned int j = 0; j < gzChatManagerClass::m_chatCommandList[i]->m_commandList.Count(); j++)
			{
				if (gzChatManagerClass::m_chatCommandList[i]->m_commandList[j] == cmd.gettok(1, ' ').GetData().GetString())
				{
					if ((evt.m_type == 0 && (gzChatManagerClass::m_chatCommandList[i]->m_type & MSG_ALL) == MSG_ALL) ||
						(evt.m_type == 1 && (gzChatManagerClass::m_chatCommandList[i]->m_type & MSG_TEAM) == MSG_TEAM) ||
						(evt.m_type == 2 && (gzChatManagerClass::m_chatCommandList[i]->m_type & MSG_PRIVATE) == MSG_PRIVATE))
					{
						aWideString retBuf, msg = cmd.gettok(2, cmd.numtok(' ') - 1, ' ').GetData();
						gzChatManagerClass::m_chatCommandList[i]->Activate(msg.GetString(), evt.m_type, evt.m_sender, evt.m_receiver, retBuf);
						if (!retBuf.IsEmpty())
							evt.m_message = retBuf;
						goto endofcmd;
					}
				}
			}
		}
endofcmd:

		// Text filter
		for (aListNode<gzTextFilterStruct>* list = this->m_textFilter.GetHead(); list != NULL; list = list->GetNext())
		{
			if (list->GetData()->enable &&
				(((list->GetData()->type & MSG_ALL) == MSG_ALL && evt.m_type == 0) ||
				((list->GetData()->type & MSG_TEAM) == MSG_TEAM && evt.m_type == 1)))
			{
				for (unsigned int j = 0; j < list->GetData()->String.Count(); j++)
				{
					bool Match = false;
					if (list->GetData()->cmpType == TYPE_NORM)
						Match = (list->GetData()->String[j] == evt.m_message) ? true : false;

					else if (list->GetData()->cmpType == TYPE_WILDCARD)
						Match = evt.m_message.Matches(list->GetData()->String[j].GetString());

					else if (list->GetData()->cmpType == TYPE_REGEX)
					{
						aRegex regex(evt.m_message.GetString());
						Match = (regex.Compile(list->GetData()->String[j]) > 0);
					}

					if (Match)
					{
						if (list->GetData()->options & OPT_LOG)
						{
							aDateTime now = aDateTime::Now();
							aFile renlog(aString::Format("renlog_%d-%d-%04d.txt", now.GetMonth(), now.GetDay(), now.GetYear()), "a");
							if (renlog.IsOpened()) // Able to open file
							{
								renlog.Write(aString::Format("[%02d:%02d:%02d] ", now.GetHour(), now.GetMinute(), now.GetSecond()));

								if (evt.m_type == 1) // Team message
									renlog.Write("[Team] ");

								if (nc_cPlayerManager::Find_Player(evt.m_sender)) // Player exists
									renlog.Write(aString::Format("%ls: ", nc_cPlayerManager::Find_Player(evt.m_sender)->PlayerName.m_Buffer));

								else // Player does not exist
									renlog.Write("(null): ");

								renlog.Write(evt.m_message.GetString());
								renlog.Write("\n");
							}
						}
						evt.Skip();
						goto endoftextfltr;
					}
				}
			}
		}
	}
endoftextfltr:

	// PM logging
	if (evt.m_type == 2 && evt.m_sender > 0 && evt.m_receiver > 0)
	{
		aDateTime now = aDateTime::Now();
		aFile renlog(aString::Format("renlog_%d-%d-%04d.txt", now.GetMonth(), now.GetDay(), now.GetYear()), "a");
		if (renlog.IsOpened()) // Able to open file
		{
			nc_cPlayer *pData[2] = {
				nc_cPlayerManager::Find_Player(evt.m_sender),
				nc_cPlayerManager::Find_Player(evt.m_receiver)
			};
			renlog.Write(aString::Format("[%02d:%02d:%02d] %ls (to %ls): %ls\n", now.GetHour(), now.GetMinute(), now.GetSecond(), pData[0] ? pData[0]->PlayerName.m_Buffer : L"(null)", pData[1] ? pData[1]->PlayerName.m_Buffer : L"(null)", evt.m_message.GetString()));

			gzLogger("_PRIVATEMSG", "%ls (to %ls): %ls", pData[0] ? pData[0]->PlayerName.m_Buffer : L"(null)", pData[1] ? pData[1]->PlayerName.m_Buffer : L"(null)", evt.m_message.GetString());
		}
	}
}
void gzChatManagerClass::Net_WolPage(gzEventNetWolPage &evt)
{
	aWideString nick = evt.m_sender;

	// Mute check
	for (unsigned int i = 0; i < this->m_muteList.Count(); i++)
	{
		if (this->m_muteList[i] == nick)
		{
			evt.Skip();
			break;
		}
	}
}

gzFloodBase::gzFloodBase()
{
	this->RegisterEvent(EVT_GAME_THINK);
}

gzChatFloodClass::gzChatFloodClass(nc_cPlayer *pData)
{
	this->RegisterEvent(EVT_PLAYER_CHAT);

	this->m_owner = pData;
	this->m_Count = 0;
	this->m_Interval = 8.0f;
}
void gzChatFloodClass::Think()
{
	SubFrameTime(this->m_Interval, (this->m_Count > 0));
	if (this->m_Interval < 0.0f)
	{
		this->m_Count = 0;
		this->m_Interval = 8.0f;
	}
}
void gzChatFloodClass::Player_Chat(gzEventPlayerChat &evt)
{
	if (evt.m_type == 0 || evt.m_type == 1)
	{
		if (evt.m_sender == this->m_owner->PlayerId)
		{
			// Flood control
			if (this->m_Count >= 5)
			{
				evt.Skip();
				return;
			}
			this->m_Count++;
		}
	}
}

gzRadioFloodClass::gzRadioFloodClass(nc_cPlayer *pData)
{
	this->RegisterEvent(EVT_PLAYER_RADIO);

	this->m_owner = pData;
	this->m_Count = 0;
	this->m_Interval = 8.0f;
}
void gzRadioFloodClass::Think()
{
	SubFrameTime(this->m_Interval, (this->m_Count > 0));
	if (this->m_Interval < 0.0f)
	{
		this->m_Interval = 8.0f;
		this->m_Count = 0;
	}
}
void gzRadioFloodClass::Player_Radio(gzEventPlayerRadio &evt)
{
	if (evt.m_sender == this->m_owner->PlayerId)
	{
		// Invalid radio id?
		if (evt.m_key < 0 || evt.m_key >= 30)
		{
			evt.Skip();
			return;
		}

		// Block custom radio id
		evt.m_radioID = nc_CNCModeSettingsDef::_mInstance->Radio_Command[evt.m_key];

		// Flood control
		gzPlayer *pData = gzPlayerManager::Find(evt.m_sender);
		if (pData)
		{
			if (pData->GetRadioCtrl()->m_Count >= 5)
			{
				evt.Skip();
				return;
			}
			pData->GetRadioCtrl()->m_Count++;
		}
	}
}


/********************/
/* Console commands */
/********************/
class MuteConsoleFunction : public gzConsoleCommand {
public:
	char *Get_Name(void)
	{
		return "mute";
	}
	char *Get_Help(void)
	{
		return "MUTE <player id> - Adds player to mute list. The list will be cleared when server restarts.";
	}
	void Activate(char *text)
	{
		if (!*text)
			return;
		nc_cPlayer *pData = nc_cPlayerManager::Find_Player(atoi(text));
		if (!pData)
			return;
		for (unsigned int i = 0; i < gzChat->m_muteList.Count(); i++)
		{
			if (gzChat->m_muteList[i] == pData->PlayerName.m_Buffer)
				return;
		}
		aWideString player = pData->PlayerName.m_Buffer;
		gzChat->m_muteList.Add(player);
		PagePlayer(pData->PlayerId, "You're now being muted.");
	}
};
MuteConsoleFunction mute;

class MuteListConsoleFunction : public gzConsoleCommand {
public:
	char *Get_Name(void)
	{
		return "mutelist";
	}
	char *Get_Help(void)
	{
		return "MUTELIST - List the nicknames in mute list.";
	}
	void Activate(char *text)
	{
		stConsole::Out("Mute list:\n");
		for (unsigned int i = 0; i < gzChat->m_muteList.Count(); i++)
			stConsole::Out("\t%ls\n", gzChat->m_muteList[i].GetString());
	}
};
MuteListConsoleFunction mutelist;

class UnmuteConsoleFunction : public gzConsoleCommand {
public:
	char *Get_Name(void)
	{
		return "unmute";
	}
	char *Get_Help(void)
	{
		return "UNMUTE <player> - Remove <player> from mute list.";
	}
	void Activate(char *text)
	{
		if (!*text)
			return;
		aWideString nick = text;
		for (unsigned int i = 0; i < gzChat->m_muteList.Count(); i++)
		{
			if (gzChat->m_muteList[i] == nick)
			{
				gzChat->m_muteList.Delete(i);

				gzPlayer *gzData = gzPlayerManager::Find(nick.GetString());
				if (gzData)
					PagePlayer(gzData->GetPlayerData()->PlayerId, "You have been unmuted.");
				break;
			}
		}
	}
};
UnmuteConsoleFunction unmute;

class TeamAdminMsgConsoleFunction : public gzConsoleCommand {
public:
	char *Get_Name(void){
		return "tamsg";
	}
	char *Get_Help(void){
		return "TAMSG <team> <message> - Sends a popup message to all <team> players.\n0 = Nod\n1 = GDI";
	}
	void Activate(char *text){
		if (!text || !*text)
			return;
		aToken Token = text;
		int Team = Token.gettok(1,' ').ToLong();
		aString msg = Token.gettok(2, Token.numtok(' ') - 1, ' ').GetData();

		if (msg.Contain("%n"))
			return;

		nc_cScTextObj *ScTextObj = nc_cScTextObj::Create();
		ScTextObj->SenderId = -1;
		ScTextObj->Type = 1;
		ScTextObj->IsPopup = true;
		ScTextObj->Message.Get_String(0, false);
		ScTextObj->Message.Convert_From(msg.GetString());
		for (nc_GenericSLNode<nc_cPlayer> *pList = nc_cPlayerManager::PlayerList->HeadNode; pList != NULL; pList = pList->NodeNext)
		{
			if (pList->NodeData->IsActive)
			{
				if (pList->NodeData->PlayerType.Get() == Team)
				{
					ScTextObj->Set_Object_Dirty_Bit(pList->NodeData->PlayerId, nc_DB_CREATION, true);
					nc_cNetwork::Send_Object_Update(ScTextObj, pList->NodeData->PlayerId);
				}
			}
		}
		ScTextObj->Set_Delete_Pending();
	}
};
TeamAdminMsgConsoleFunction teamamsg;
