#include "g_local.h"

extern void	InitMover( gentity_t *ent );
extern gentity_t	*G_TestEntityPosition( gentity_t *ent );
void func_usable_use (gentity_t *self, gentity_t *other, gentity_t *activator);
void func_wait_return_solid( gentity_t *self )
{
	//once a frame, see if it's clear.
	self->clipmask = CONTENTS_BODY;
	if ( !(self->spawnflags&16) || G_TestEntityPosition( self ) == NULL )
	{
		trap_SetBrushModel( self, self->model );
		InitMover( self );
		VectorCopy( self->s.origin, self->s.pos.trBase );
		VectorCopy( self->s.origin, self->r.currentOrigin );
		self->r.svFlags &= ~SVF_NOCLIENT;
		self->s.eFlags &= ~EF_NODRAW;
		self->use = func_usable_use;
		self->clipmask = 0;
		/*
		if ( self->s.eFlags & EF_ANIM_ONCE )
		{//Start our anim
			self->s.frame = 0;
		}
		*/
		if ( !(self->spawnflags&1) && !(self->spawnflags & 1024) )
		{//START_OFF doesn't effect area portals
			trap_AdjustAreaPortalState( self, qfalse );
		}
	}
	else
	{
		self->clipmask = 0;
		self->think = func_wait_return_solid;
		self->nextthink = level.time + FRAMETIME;
	}
}

void func_usable_think( gentity_t *self )
{
	if ( self->spawnflags & 8 )
	{
		//self->r.svFlags |= SVF_PLAYER_USABLE;	//Replace the usable flag
		self->use = func_usable_use;
		self->think = 0/*NULL*/;
		self->nextthink = -1;
	}
}

void func_usable_use (gentity_t *self, gentity_t *other, gentity_t *activator)
{//Toggle on and off
	#ifdef XTRA
	//Remap shader
	if(self->targetShaderName && self->targetShaderNewName) {
		float f = level.time * 0.001;
		AddRemap(self->targetShaderName, self->targetShaderNewName, f);
		trap_SetConfigstring(CS_SHADERSTATE, BuildShaderStateConfig());
	}
	#endif


	//RPG-X | GSIO01 | 09/05/2009:
	if(self->spawnflags & 256) { // ADMINS_ONLY
		if(!IsAdmin(activator))
			return;
	}

	if(self->flags & FL_LOCKED) // Deactivated
		return;

	if ( self->spawnflags & 8 )		// fixme: uurrgh!!!!  that's disgusting
	{//ALWAYS_ON
		//Remove the ability to use the entity directly
		//self->r.svFlags &= ~SVF_PLAYER_USABLE;
		//also remove ability to call any use func at all!
		self->use = 0/*NULL*/;
		
		if(self->target && self->target[0])
		{
			if(self->spawnflags & 512)
				G_UseTargets(self, self);
			else
				G_UseTargets(self, activator);
		}
		
		if ( self->wait )
		{
			self->think = func_usable_think;
			self->nextthink = level.time + ( self->wait * 1000 );
		}

		return;
	}
	else if ( !self->count )
	{//become solid again
		self->count = 1;
		func_wait_return_solid( self );
	}
	else
	{
		self->s.solid = 0;
		self->r.contents = 0;
		self->clipmask = 0;
		self->r.svFlags |= SVF_NOCLIENT;
		self->s.eFlags |= EF_NODRAW;
		self->count = 0;

		if(self->target && self->target[0])
		{
			if(self->spawnflags & 512)
				G_UseTargets(self, self);
			else
				G_UseTargets(self, activator);
		}
		self->think = 0/*NULL*/;
		self->nextthink = -1;
		if ( !(self->spawnflags&1) && !(self->spawnflags & 1024))
		{//START_OFF doesn't effect area portals
			trap_AdjustAreaPortalState( self, qtrue );
		}
	}
}

void func_usable_pain(gentity_t *self, gentity_t *attacker, int damage)
{
	self->use( self, attacker, attacker );
}

void func_usable_die(gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod)
{
	self->takedamage = qfalse;
	self->use( self, inflictor, attacker );
}

/*QUAKED func_usable (0 .5 .8) ? STARTOFF AUTOANIM x ALWAYS_ON NOBLOCKCHECK x x x ADMIN_ONLY NO_ACTIVATOR NO_AREAPORTAL DEACTIVATED
START_OFF - the wall will not be there
AUTOANIM - If useing an md3, it will animate
ALWAYS_ON - Doesn't toggle on and off when used, just fires target
NOBLOCKCHECK - Will NOT turn on while something is inside it unless this is checked
ADMIN_ONLY - can only be used by admins
NO_ACTIVATOR - use the ent itself instead the player as activator
NO_AREAPORTAL - don't affect areaportals
DEACTIVATED - start deactivated

A bmodel that can be used directly by the player's "activate" button

"targetname"	When used, will toggle on and off
"target"		Will fire this target every time it is toggled OFF
"model2"		.md3 model to also draw
"color"			constantLight color
"light"			constantLight radius
"wait"			amount of time before the object is usable again (only valid with ALWAYS_ON flag)
"health"		if it has health, it will be used whenever shot at/killed - if you want it to only be used once this way, set health to 1
"messageNum"	the number relating to the message string that will display when the player scans this usable with a tricorder

"team" - This can only be used by this team (2 = blue, 1 = red)
*/

void SP_func_usable( gentity_t *self ) 
{
	trap_SetBrushModel( self, self->model );
	InitMover( self );
	VectorCopy( self->s.origin, self->s.pos.trBase );
	VectorCopy( self->s.origin, self->r.currentOrigin );

	//Com_Printf( "PointOrigin: { %f, %f, %f }\n", self->s.origin[0], self->s.origin[1], self->s.origin[2] );

	self->count = 1;
	if (self->spawnflags & 1)
	{
		self->s.solid = 0;
		self->r.contents = 0;
		self->clipmask = 0;
		self->r.svFlags |= SVF_NOCLIENT;
		self->s.eFlags |= EF_NODRAW;
		self->count = 0;
	}

	if (self->spawnflags & 2)
	{
		self->s.eFlags |= EF_ANIM_ALLFAST;
	}

	/*
	if (self->spawnflags & 4)
	{//FIXME: need to be able to do change to something when it's done?  Or not be usable until it's done?
		self->s.eFlags |= EF_ANIM_ONCE;
	}
	*/

	self->use = func_usable_use;

	if ( self->health )
	{
		self->takedamage = qtrue;
		self->die = func_usable_die;
		self->pain = func_usable_pain;
	}
	
	//if the map has a usables file, get the data of of that
	if ( rpg_scannablePanels.integer )
	{
		if ( level.hasScannableFile )
		{
			int i = 0;
			int j = 0;
			int	messageNum=0;
			int entityNum = self - g_entities;

			self->s.weapon = 0;

			//TiM - check for a message integer
			G_SpawnInt( "messageNum", "0", &messageNum );

			if(self->luaEntity)
				messageNum = self->damage;

			if ( messageNum > 0 )
			{
				while( i < MAX_SCANNABLES )
				{
					if ( level.g_scannables[i] == 0 )
						break;

					if ( level.g_scannables[i] == messageNum )
					{
						self->s.weapon = i+1; //+1 offset so 0 becomes the default error
						break;
					}

					i++;
				}
			}

			//if no match was found, try the entities list if possible
			if ( self->s.weapon == 0 && level.hasEntScannableFile )
			{
				i=0;
				while( i < MAX_ENTSCANNABLES )
				{
					if ( !level.g_entScannables[i][0] && !level.g_entScannables[i][1] )
						break; 

					if( level.g_entScannables[i][0] == entityNum )
					{
						j=0;
						while ( j < MAX_SCANNABLES )
						{
							if ( level.g_scannables[j] == 0 )
								break;

							if ( level.g_scannables[j] == level.g_entScannables[i][1] )
							{
								self->s.weapon = j+1;
								i = MAX_ENTSCANNABLES+1; //break the outer loop hehe
								break;
							}

							j++;
						}
					}
					
					i++;
				}
			}

			if ( self->s.weapon > 0 )
			{
				self->s.eType = ET_TRIC_STRING;
				
				//DEBUG
				//G_Printf( S_COLOR_RED "Entry found for entity: %i!\n", self-g_entities );

				//Set bounding box for maxs/mins on tricorder rendering
				VectorCopy( self->r.mins, self->s.origin2 ); //mins dimension
				VectorCopy( self->r.maxs, self->s.angles2 ); //maxs
			}
		}
		else
		{
			//TiM: Humm... hopefully in my infinite hackiness, this won't screw anything up lol.
			//Apparently this is the cause of Atlantis dying. The amount of scannable ents is making too
			//much data for Q3 to handle.
			//I've CVAR'd it off for now. Hopefully if/when the map gets fixed, it can be put back online.
			if ( self->message ) { //If there is a message here, we gotz to get it over to CG
				self->s.eType = ET_TRIC_STRING; //Set a special type we can use to identify this over on CG

				//Com_Printf( S_COLOR_RED "USABLE MESSAGE: %s\n", self->message );
				self->s.time2 = G_TricStringIndex( self->message ); //encode the message into an info string
				//Set bounding box for maxs/mins on tricorder rendering
				VectorCopy( self->r.mins, self->s.origin2 ); //mins dimension
				VectorCopy( self->r.maxs, self->s.angles2 ); //maxs
			}
		}
	}

	if(self->spawnflags & 2048)
		self->flags ^= FL_LOCKED;

	trap_LinkEntity (self);

	level.numBrushEnts++;
}

/*
G_LoadMapConfigurations
*/
/**
*	\brief loads usable strings
*	\return sucessfully loaded?
*	\author Ubergames - TiM
*/
#ifdef Q3_VM
qboolean G_SetupUsablesStrings( void )
{
	char			serverInfo[MAX_TOKEN_CHARS];
	char			fileRoute[MAX_QPATH];
	char			buffer[20000];
	int				file_len;
	char			*textPtr, *token;
	fileHandle_t	f;
	int				i, j;

	level.hasScannableFile = qfalse;
	level.hasEntScannableFile = qfalse;

	//get the map name out of the server data
	trap_GetServerinfo( serverInfo, sizeof( serverInfo ) );

	//setup the file route
	Com_sprintf( fileRoute, sizeof( fileRoute ), "maps/%s.usables", Info_ValueForKey( serverInfo, "mapname" ) );

	file_len = trap_FS_FOpenFile( fileRoute, &f, FS_READ );

	//It's assumed most maps won't have this feature, so just exit 'gracefully'
	if ( file_len<=1 )
	{
		trap_FS_FCloseFile( f );
		//G_Printf( S_COLOR_YELLOW "WARNING: No file named %s was found.\n", fileRoute );
		return qfalse;
	}

	//fill the buffer with the file data
	memset( &buffer, 0, sizeof( buffer ) );
	trap_FS_Read( buffer, file_len, f );
	buffer[file_len] = '0';

	trap_FS_FCloseFile( f );
	
	if ( !buffer[0] )
	{
		G_Printf( S_COLOR_RED "ERROR: Attempted to load %s, but no data was inside!\n", fileRoute );
		return qfalse;
	}

	G_Printf( "Usables file %s located. Proceeding to load scan data.\n", fileRoute );

	COM_BeginParseSession();
	textPtr = buffer;

	i = 0;	//used for the main arrays indices

	while( 1 )
	{
		token = COM_Parse( &textPtr );
		if ( !token[0] )
			break;

		if ( !Q_strncmp( token, "UsableDescriptions", 18 ) )
		{
			token = COM_Parse( &textPtr );
			if ( Q_strncmp( token, "{", 1 ) != 0 )
			{
				G_Printf( S_COLOR_RED "ERROR: UsableDescriptions had no opening brace ( { )!\n", fileRoute );
				continue;
			}

			level.hasScannableFile = qtrue;

			token = COM_Parse( &textPtr );

			//expected format is 'id' <space> 'string'
			while ( Q_strncmp( token, "}", 1 ) )
			{
				if ( !token[0] )
					break;

				if ( !Q_strncmp( token, "UsableEntities", 14 ) )
				{
					token = COM_Parse( &textPtr );
					if ( Q_strncmp( token, "{", 1 ) )
					{
						G_Printf( S_COLOR_RED "ERROR: UsableEntities had no opening brace ( { )!\n", fileRoute );
						continue;
					}

					level.hasEntScannableFile = qtrue;

					token = COM_Parse( &textPtr );

					j = 0;
					while( Q_strncmp( token, "}", 1 ) )
					{
						if ( !token[0] )
							break;

						if ( token[0] != 'e' )
						{
							SkipRestOfLine( &textPtr );
							continue;
						}

						token++; //skip the 'e'

						level.g_entScannables[j][0] = atoi( token );
						token = COM_ParseExt( &textPtr, qfalse );
						level.g_entScannables[j][1] = atoi( token );

						//there's no way clients are scannable in here, so just validate the entry b4 proceeding
						if ( level.g_entScannables[j][0] > MAX_CLIENTS-1 && level.g_entScannables[j][1] > 0 )
							j++;

						token = COM_Parse( &textPtr );
					}
				}
				else
				{
					level.g_scannables[i] = atoi( token );

					//ensure a valid number was passed, else ignore it
					if ( level.g_scannables[i] > 0 )
						i++;

					//we don't need the text on the server side
					SkipRestOfLine( &textPtr );

					token = COM_Parse( &textPtr );
				}
			}
		}
	}

	return qtrue;
}
#else
qboolean G_SetupUsablesStrings( void )
{
	char			*serverInfo;
	char			fileRoute[MAX_QPATH];
	char			*buffer;
	int				file_len;
	char			*textPtr, *token;
	fileHandle_t	f;
	int				i, j;

	level.hasScannableFile = qfalse;
	level.hasEntScannableFile = qfalse;

	serverInfo = (char *)malloc(MAX_TOKEN_CHARS * sizeof(char));
	if(!serverInfo) {
		return qfalse;
	}	

	//get the map name out of the server data
	trap_GetServerinfo( serverInfo, MAX_TOKEN_CHARS * sizeof(char) );

	//setup the file route
	Com_sprintf( fileRoute, sizeof( fileRoute ), "maps/%s.usables", Info_ValueForKey( serverInfo, "mapname" ) );

	file_len = trap_FS_FOpenFile( fileRoute, &f, FS_READ );

	free(serverInfo);

	//It's assumed most maps won't have this feature, so just exit 'gracefully'
	if ( file_len<=1 )
	{
		trap_FS_FCloseFile( f );
		//G_Printf( S_COLOR_YELLOW "WARNING: No file named %s was found.\n", fileRoute );
		return qfalse;
	}

	buffer = (char *)malloc(32000 * sizeof(char));
	if(!buffer) {
		trap_FS_FCloseFile(f);
		return qfalse;
	}

	//fill the buffer with the file data
	trap_FS_Read( buffer, file_len, f );
	buffer[file_len] = '0';

	trap_FS_FCloseFile( f );
	
	if ( !buffer[0] )
	{
		G_Printf( S_COLOR_RED "ERROR: Attempted to load %s, but no data was inside!\n", fileRoute );
		free(buffer);
		return qfalse;
	}

	G_Printf( "Usables file %s located. Proceeding to load scan data.\n", fileRoute );

	COM_BeginParseSession();
	textPtr = buffer;

	i = 0;	//used for the main arrays indices

	while( 1 )
	{
		token = COM_Parse( &textPtr );
		if ( !token[0] )
			break;

		if ( !Q_strncmp( token, "UsableDescriptions", 18 ) )
		{
			token = COM_Parse( &textPtr );
			if ( Q_strncmp( token, "{", 1 ) != 0 )
			{
				G_Printf( S_COLOR_RED "ERROR: UsableDescriptions had no opening brace ( { )!\n", fileRoute );
				continue;
			}

			level.hasScannableFile = qtrue;

			token = COM_Parse( &textPtr );

			//expected format is 'id' <space> 'string'
			while ( Q_strncmp( token, "}", 1 ) )
			{
				if ( !token[0] )
					break;

				if ( !Q_strncmp( token, "UsableEntities", 14 ) )
				{
					token = COM_Parse( &textPtr );
					if ( Q_strncmp( token, "{", 1 ) )
					{
						G_Printf( S_COLOR_RED "ERROR: UsableEntities had no opening brace ( { )!\n", fileRoute );
						continue;
					}

					level.hasEntScannableFile = qtrue;

					token = COM_Parse( &textPtr );

					j = 0;
					while( Q_strncmp( token, "}", 1 ) )
					{
						if ( !token[0] )
							break;

						if ( token[0] != 'e' )
						{
							SkipRestOfLine( &textPtr );
							continue;
						}

						token++; //skip the 'e'

						level.g_entScannables[j][0] = atoi( token );
						token = COM_ParseExt( &textPtr, qfalse );
						level.g_entScannables[j][1] = atoi( token );

						//there's no way clients are scannable in here, so just validate the entry b4 proceeding
						if ( level.g_entScannables[j][0] > MAX_CLIENTS-1 && level.g_entScannables[j][1] > 0 )
							j++;

						token = COM_Parse( &textPtr );
					}
				}
				else
				{
					level.g_scannables[i] = atoi( token );

					//ensure a valid number was passed, else ignore it
					if ( level.g_scannables[i] > 0 )
						i++;

					//we don't need the text on the server side
					SkipRestOfLine( &textPtr );

					token = COM_Parse( &textPtr );
				}
			}
		}
	}

	free(buffer);
	return qtrue;
}
#endif

