#include "ns.h"


#include <sdk_util.h> //useful almost everywhere
#include <usercmd.h>
#include <entity_state.h>

CSpawn ns_spawnpoints;
CPlayer g_player[33];
edict_t *player_edicts[33];

BOOL CheckForPublic(const char *publicname);

int gmsgHudText2=0;
int ChangeclassForward = -1;
int BuiltForward = -1;
int SpawnForward = -1;
int TeamForward = -1;
// Index of last entity hooked in CreateNamedEntity
int iCreateEntityIndex;
BOOL iscombat;
int gmsgScoreInfo=0;

// Module is attaching to AMXX
void OnAmxxAttach()
{
	MF_AddNatives(ns_misc_natives);
	MF_AddNatives(ns_pdata_natives);
}

// All plugins have loaded (probably around Spawning worldspawn..
void OnPluginsLoaded()
{

	gmsgHudText2 = GET_USER_MSG_ID(&Plugin_info,"HudText2",NULL);
	// Check the map name and see if it's combat or not.
	iscombat=FALSE;
	char mapname[255];
	strcpy(mapname,STRING(gpGlobals->mapname));
	if ((mapname[0]=='c' || mapname[0]=='C') && (mapname[1]=='o' || mapname[1]=='O') && mapname[2]=='_')
		iscombat=TRUE;

	ChangeclassForward = MF_RegisterForward("client_changeclass", ET_IGNORE, FP_CELL, FP_CELL, FP_CELL, FP_CELL, FP_DONE);
	// No sense in this if it's combat..
	if (!iscombat) {
		if (CheckForPublic("client_built")) {
			BuiltForward = MF_RegisterForward("client_built", ET_IGNORE, FP_CELL, FP_CELL, FP_CELL, FP_CELL, FP_DONE);
			g_pengfuncsTable_Post->pfnAlertMessage=AlertMessage_Post;
			g_pengfuncsTable->pfnCreateNamedEntity=CreateNamedEntity;
		} else {
			g_pengfuncsTable_Post->pfnAlertMessage=NULL;
			g_pengfuncsTable->pfnCreateNamedEntity=NULL;
		}
	} else {
		// no need for these hooks in co
		g_pengfuncsTable_Post->pfnAlertMessage=NULL;
		g_pengfuncsTable->pfnCreateNamedEntity=NULL;
	}
	SpawnForward = MF_RegisterForward("client_spawn",ET_IGNORE,FP_CELL/*id*/,FP_DONE);
	TeamForward = MF_RegisterForward("client_changeteam",ET_IGNORE,FP_CELL/*id*/,FP_CELL/*new team*/,FP_CELL/*old team*/,FP_DONE);
}



int DispatchSpawn(edict_t *pEntity) 
{
	// Everything starting up:
	//   - Reset CPlayer classes
	//   - Reset CSpawn classes
	if (FStrEq(STRING(pEntity->v.classname),"worldspawn"))
	{
		int i;
		for (i=0;i<=32;i++)
		{
			CPlayer *player = GET_PLAYER_I(i);
			player->Reset();
		}
		ns_spawnpoints.clear();
	}
	else if (FStrEq(STRING(pEntity->v.classname),"info_player_start"))
	{
		// Mark down the ready room spawn point.
		ns_spawnpoints.put(0,pEntity->v.origin);
	}
	else if (FStrEq(STRING(pEntity->v.classname),"info_team_start"))
	{
		// Mark down the team based spawn point.
		ns_spawnpoints.put(pEntity->v.team,pEntity->v.origin);
	}
	RETURN_META_VALUE(MRES_IGNORED, 0);
}


void ServerActivate(edict_t *pEdictList, int edictCount, int clientMax)
{
	// Mark down proper edicts (fixes INDEXENT() bug).
	// Reset CPlayer classes (again?)
	for(int i = 1; i <= gpGlobals->maxClients;i++) 
	{
		player_edicts[i]=pEdictList + i;
		CPlayer *player = GET_PLAYER_I(i);
		player->edict=pEdictList + i;
		//player->index=i;
		player->pev=&player->edict->v;
		player->oldimpulse=0;
		player->Reset();
		player->connected=false;
	}
	RETURN_META(MRES_IGNORED);
}
void PlayerPreThink(edict_t *pEntity)
{
	CPlayer *player = GET_PLAYER_E(pEntity);
	player->PreThink();
	RETURN_META(MRES_IGNORED);
}
void PlayerPreThink_Post(edict_t *pEntity)
{
	CPlayer *player = GET_PLAYER_E(pEntity);
	player->PreThink_Post();
	RETURN_META(MRES_IGNORED);
}
void PlayerPostThink_Post(edict_t *pEntity)
{
	CPlayer *player = GET_PLAYER_E(pEntity);
	player->PostThink_Post();
	RETURN_META(MRES_IGNORED);
}

 
// Parse log messages here for any desired information (structure_built, etc.)
// The following logs are needed:
// name<CID><AUTHID><TEAM> triggered "structure_built" (type "type") -- client_built
// name<CID><AUTHID><TEAM> changed role to "class" -- client_changeclass
void AlertMessage_Post(ALERT_TYPE atype, char *szFmt, ...)
{
	if (atype != at_logged)
		RETURN_META(MRES_IGNORED);
	va_list LogArg;
	char *sz, *message;
	const char *b;
	char szParm[5][128];
	int argc,len;
	va_start(LogArg, szFmt);
	sz = va_arg(LogArg, char *);
	va_end(LogArg);
	message = sz;
	b=message;
	argc=0;
	// Parse the damn message
	while (*b && *b!='\0' && argc<5)
	{

		len=0;
		if (*b == '"')
		{
			b++; // Skip over the "
			while (*b && *b != '"' && len < 127)
			{
				szParm[argc][len]=*b;
				b++;
				len++;
			}
			//*szParm='\0';
			szParm[argc][len]='\0';
			if (*b && *b == '"' && *b+1 != '\0' && *b+2 != '\0')
			{
				b+=2;
				argc++;
			}
			else
			{
				argc++;
				break;
			}
		}
		else if (*b == '(')
		{
			b++; // Skip over the (
			while (*b && *b != ')' && len < 127)
			{
				szParm[argc][len]=*b;
				b++;
				len++;
			}
			szParm[argc][len]='\0';
			if (*b && *b == ')' && *b+1 != '\0' && *b+2 != '\0')
			{
				b+=2;
				argc++;
			}
			else
			{
				argc++;
				break;
			}
		}
		else
		{
			while (*b && *b != '"' && *b != '(' && len < 127)
			{
				szParm[argc][len]=*b;
				b++;
				len++;
			}
			szParm[argc][len]='\0';
			if (*b != '"' && *b != '(' && *b != '\0' && *b+1 != '\0' && *b+2 != '\0')
			{
				b+=2;
				argc++;
			}
			else
			{
				argc++;
				if (*b == '\0')
					break;
			}

		}
		
		
	}
/*
	if (argc == 3) // changed role to = 3 long
	{
		if (FStrEq((const char *)szParm[1],"changed role to "))
		{
			int index=LogToIndex(szParm[0]);
			if (!index)
				RETURN_META(MRES_IGNORED);
			CPlayer *player = GET_PLAYER_I(index);
			int iImpulse=0;
			int iClass=1;
			if (INDEXENT2(index)->v.team != 0)
			{
				if (FStrEq((const char *)szParm[2],"gestate"))
				{
					iImpulse = player->oldpev.impulse;
				}
				if (FStrEq((const char *)szParm[2],"none"))
					iClass=0;
				if (iClass > 0)
				{
					ns2amx_changeclass.execute(index,player->oldpev.iuser3,player->pev->iuser3,iImpulse);
				}
			}
		}
	}
	else */
	if (argc == 4) // structure_built / structure_destroyed are 4 long
	{
		//"NAME<CID><AUTH><TEAM>" triggered "structure_built" (type "TYPE")
		if (FStrEq((const char *)szParm[2],"structure_built"))
		{
			int index=LogToIndex(szParm[0]);
			if (!index)
				RETURN_META(MRES_IGNORED);
			CPlayer *player = GET_PLAYER_I(index);

			int iForward=0;
			int iType=player->pev->impulse;
			if (FStrEq((const char *)szParm[3],"type \"team_hive\""))
			{
				iForward=2;
				iCreateEntityIndex=Find_Building_Hive();
			}
			else if (FStrEq((const char *)szParm[3],"type \"offensechamber\""))
			{
				iForward=2;
			}
			else if (FStrEq((const char *)szParm[3],"type \"movementchamber\""))
			{
				iForward=2;
			}
			else if (FStrEq((const char *)szParm[3],"type \"sensorychamber\""))
			{
				iForward=2;
			}
			else if (FStrEq((const char *)szParm[3],"type \"defensechamber\""))
			{
				iForward=2;
			}
			else if (FStrEq((const char *)szParm[3],"type \"alienresourcetower\""))
			{
				iForward=2;
			}
			else
			{
				iForward = 1;
			}
//			ns2amx_built.execute(index,iCreateEntityIndex,iForward,iType);
			if (BuiltForward != -1) {
				MF_ExecuteForward(BuiltForward, index, FStrEq((const char *)szParm[3],"type \"weapon_mine\"") ? 0 : iCreateEntityIndex, iForward, iType);
			}
			iCreateEntityIndex=0;
		}
	}
	RETURN_META(MRES_IGNORED);
}

// We hook newly created entities here.
// This is where we check for client_built created entities.
edict_t* CreateNamedEntity(int className)
{
	if (iscombat)
		RETURN_META_VALUE(MRES_IGNORED,0);
	edict_t *pEntity;
	// Incase another plugin supercedes/overrides, use their returned value here.
	// (Untested).
	if (gpMetaGlobals->status >= MRES_OVERRIDE)
	{
		pEntity=META_RESULT_OVERRIDE_RET(edict_t *);
		iCreateEntityIndex=ENTINDEX(pEntity);
		RETURN_META_VALUE(MRES_IGNORED, false);
	}
	else
	{
		pEntity=CREATE_NAMED_ENTITY(className);
		if (!FNullEnt(pEntity))
		{
			iCreateEntityIndex=ENTINDEX(pEntity);
			RETURN_META_VALUE(MRES_SUPERCEDE,pEntity);
		}
		RETURN_META_VALUE(MRES_SUPERCEDE,pEntity);
	}
	RETURN_META_VALUE(MRES_IGNORED,false);
}

// Map is changing/server is shutting down.
// We do all cleanup routines here, since, as noted in metamod's dllapi
// ServerDeactivate is the very last function called before the server loads up a new map.
void ServerDeactivate(void)
{
	for (int i=1;i<=gpGlobals->maxClients;i++)
	{
		CPlayer *player = GET_PLAYER_I(i);
		if (player->connected)
			player->Disconnect();
	}
	ns_spawnpoints.clear();
	RETURN_META(MRES_IGNORED);
}

// Reset player data here..
qboolean ClientConnect(edict_t *pEntity, const char *pszName, const char *pszAddress, char szRejectReason[ 128 ])
{
	// Client's connecting.  Freshen up his save data, and mark him as being connected.
	CPlayer *player = GET_PLAYER_E(pEntity);
	player->Connect();
	RETURN_META_VALUE(MRES_HANDLED,0);
}
void ClientDisconnect(edict_t *pEntity)
{
	// Client is disconnecting, clear all his saved information.
	CPlayer *player = GET_PLAYER_E(pEntity);
	player->Disconnect();
	RETURN_META(MRES_HANDLED);
}

// NS resets pev->fov every single frame, but this is called right before the data is sent to the client.
// Reset FOV if we need to.
void UpdateClientData( const struct edict_s *ent, int sendweapons, struct clientdata_s *cd )
{
	edict_t *pEntity = (edict_t*)ent;
	CPlayer *player = GET_PLAYER_E(pEntity);
	if (player->foved)
		pEntity->v.fov = player->fov;

	RETURN_META(MRES_HANDLED);

}
int LogToIndex(char logline[128])
{
	char *cname;
	// Format of log line:
	// name<CID><auth><team>
	// We need to find their ID from their name...
	int x,y=0;
	char cindex[64];
	// first we find the location of the start of the index
	// Name can contain <>'s, so we go from the end up.
	for (x=strlen(logline);x>=0;x--)
	{
		if (logline[x]=='<')
		{
			y++;
			if (y==3)
			{
				y=x;
				break;
			}
		}
	}
	// We found the end of the name, now copy the rest down.
	y--;
	x=0;
	while (x<=y)
	{
		cindex[x]=logline[x];
		x++;
	}
	cindex[x]='\0';
	// Now we have their name, now cycle through all players to find which index it is
	for (x=1;x<=gpGlobals->maxClients;x++)
	{
		cname=strdup(cindex);
		if (!FNullEnt(INDEXENT2(x)))
		{
			if (FStrEq(cname,STRING(INDEXENT2(x)->v.netname)))
			{
				return x;
			}
		}
	}
	return 0;
}
int Find_Building_Hive(void)
{
	edict_t *pEntity=NULL;
	while ((pEntity = UTIL_FindEntityByString(pEntity,"classname","team_hive")))
	{
		if (pEntity->v.health > 0 && pEntity->v.solid > 0 && pEntity->v.fuser1 < 1000)
		{
			return ENTINDEX(pEntity);
		}
	}
	return 0;
}




int AMX_MAKE_STRING(AMX *oPlugin, cell tParam, int &iLength)
{
	char *szNewValue = MF_GetAmxString(oPlugin, tParam, 0, &iLength);
	return ALLOC_STRING(szNewValue);
}

// Makes a char pointer out of an AMX cell.
char *AMX_GET_STRING(AMX *oPlugin, cell tParam, int &iLength) 
{
	char *szNewValue = MF_GetAmxString(oPlugin, tParam, 0, &iLength);
	return (char*)STRING(ALLOC_STRING(szNewValue));
}
edict_t	*UTIL_PlayerByIndexE( int playerIndex )
{

	if ( playerIndex > 0 && playerIndex <= gpGlobals->maxClients )
	{
		edict_t *pPlayerEdict = INDEXENT2( playerIndex );
		if ( pPlayerEdict && !pPlayerEdict->free )
		{
			return pPlayerEdict;
		}
	}
	
	return NULL;
}
edict_t *UTIL_FindEntityByString(edict_t *pentStart, const char *szKeyword, const char *szValue)
{
	edict_t *pentEntity;
	pentEntity=FIND_ENTITY_BY_STRING(pentStart, szKeyword, szValue);
	if(!FNullEnt(pentEntity))
		return pentEntity;
	return NULL;
}

BOOL CheckForPublic(const char *publicname)
{
	AMX* amx;
	char blah[64];
	strncpy(blah,publicname,63);
	int iFunctionIndex;
	int i=0;
	// Loop through all running scripts
	while((amx=MF_GetScriptAmx(i++))!=NULL)
	{ 
		// Scan for public
		if (MF_AmxFindPublic(amx, blah, &iFunctionIndex) == AMX_ERR_NONE)
		{
			// Public was found.
			return TRUE;
		}
	}

	return FALSE; // no public found in any loaded script
}