#include "p_global.h"
#include "p_user.h"



void *DLL_FindFunction(char *funcName);


static char *ignoreEList[200];
static int   ignoreECount;

///////////////////////////////////////////////////////////////////////////////
//
///////////////////////////////////////////////////////////////////////////////
#ifdef	FIXME_WHEN_NEEDED
static void LoadIgnoreEntityList (void)
{
	FILE *f;
	char  tempStr[200];

	ignoreECount = 0;

	if (gi.FS_Open ("ignore.dat", &f) < 0)
		return;

	while (FS_ScanF (f, "%s", tempStr) != EOF)
	{
		ignoreEList [ignoreECount] = Z_TagMalloc (strlen (tempStr) + 1, TAG_LEVEL);
		strcpy (ignoreEList [ignoreECount], tempStr);
		ignoreECount++;
	}

	gi.FS_Close (f);

	Con_DPrintf ("Ignoring %d entities\n", ignoreECount);
}

///////////////////////////////////////////////////////////////////////////////
//
///////////////////////////////////////////////////////////////////////////////

static int IgnoreEntity (char *str)
{
	int i;

	for (i = 0; i < ignoreECount; i++)
	{
		if (!stricmp(str, ignoreEList[i]))
			return	TRUE;
	}

	return	FALSE;
}
#endif

///////////////////////////////////////////////////////////////////////////////
//
///////////////////////////////////////////////////////////////////////////////

static char *Sent_EpairEdict(char *data, edict_t *ent)
{
	userEpair_t  tpair[100];
	int          tpair_count;
	qboolean	init;
	char		keyname[256];
	char       *com_token;
	int			n, i;

	init = false;
	tpair_count = 0;

	// go through all the dictionary pairs
	while (1)
	{	
		com_token = COM_Parse (&data);

		// see if we are done
		if (com_token[0] == '}') break;

		if (!data)
		{
			Sys_Error ("Sent_ParseEdict: EOF without closing brace");
		}
		
		// make sure we have room left in tpair
		if (tpair_count >= 100)
		{
			Sys_Error("Sent_EpairEdict: more than 100 epairs found.");
		}

        // FIXME: change light to _light to get rid of this hack
        if (!strcmp(com_token, "light"))
		{
			// hack for single light def
	        strcpy (com_token, "light_lev");
		}

		strcpy (keyname, com_token);

		// another hack to fix heynames with trailing spaces
		n = strlen(keyname);
		while (n && keyname[n-1] == ' ')
		{
			keyname[n-1] = 0;
			n--;
		}

	// parse value	
		com_token = COM_Parse (&data);

		if (!data)
			Sys_Error ("ED_ParseEntity: EOF without closing brace");

		if (com_token[0] == '}')
			Sys_Error ("ED_ParseEntity: closing brace without data");

		init = true;	

// keynames with a leading underscore are used for utility comments,
// and are immediately discarded by quake
		if (keyname[0] == '_')
		{
			// amw - I need the _color key on some of the entities to specify color
			if (stricmp(keyname,"_color") != 0)
				continue;
		}
		// fill in epair
		tpair[tpair_count].key   = (char *) gi.TagMalloc (strlen(keyname) + 1, TAG_LEVEL);
		tpair[tpair_count].value = (char *) gi.TagMalloc(strlen(com_token) + 1, TAG_LEVEL);

		// testing only
        // tpair[tpair_count].key   = malloc(strlen(keyname) + 1);
		// tpair[tpair_count].value = malloc(strlen(com_token) + 1);

		strcpy(tpair[tpair_count].key, keyname);
		strcpy(tpair[tpair_count].value, com_token);

		tpair_count++;
	}

	// ok set up epair in entity
	ent->epair = (userEpair_t *) gi.TagMalloc((tpair_count + 1) * sizeof(userEpair_t), TAG_LEVEL);

	// copy pointers
	for (i = 0; i < tpair_count; i++)
	{
		ent->epair[i].key   = tpair[i].key;
		ent->epair[i].value = tpair[i].value;
	}

	// make sure end ends in NULL
	ent->epair[tpair_count].key   = NULL;
	ent->epair[tpair_count].value = NULL;

	return data;
}

///////////////////////////////////////////////////////////////////////////////
//
///////////////////////////////////////////////////////////////////////////////

static char *Sent_GetValue(userEpair_t *epair, char *key)
{
	int i;

	for (i = 0; epair[i].key; i++)
	{
		if (!Q_strcasecmp(epair[i].key, key) )
		{
			return(epair[i].value);
		}
	}

	return(NULL);
}

///////////////////////////////////////////////////////////////////////////////
//
///////////////////////////////////////////////////////////////////////////////

static void Sent_BasicEpair(edict_t *ent)
{
	int i;
	char *key, *val;

	for (i = 0; ent->epair[i].key; i++)
	{
		key = ent->epair[i].key;
		val = ent->epair[i].value;

	    if (!Q_strcasecmp(key, "origin"))
	    {
		    sscanf(val, "%f%f%f", 
			    &ent->s.origin[0],
			    &ent->s.origin[1],
			    &ent->s.origin[2]);
	    }

	    // angle
	    else if (!Q_strcasecmp(key, "angle"))
	    {
		    ent->s.angles[0] = 0.0;
		    ent->s.angles[1] = atof(val);
		    ent->s.angles[2] = 0.0;
	    }

	    // spawnflags
	    else if (!Q_strcasecmp(key, "spawnflags"))
		    ent->spawnflags = atol(val);

		// model, for brush models
	    else if (!Q_strcasecmp(key, "model"))
			ent->modelName = val;

		else if (!Q_strcasecmp (key, "target"))
			ent->target = val;

		else if (!Q_strcasecmp (key, "targetname"))
			ent->targetname = val;

		//&&& AMW 6.11.98 - link entities to their parent entities if defined
		else if (!Q_strcasecmp (key, "parenttarget"))  
			ent->parentname	= val;
		
	    //&&& AMW 6.12.98 - generic linking/grouping key
		else if (!Q_strcasecmp(key, "group"))
			ent->groupname = val;

		// TEMPORARY HACK... EVENTUALLY NEEDS TO BE REMOVED
		else if (!Q_strcasecmp(key, "team"))
			ent->groupname = val;

	    //&&& AMW 6.12.98 - generic linking/grouping key
		else if (!Q_strcasecmp(key, "triggerindex"))
			ent->triggerIndex = atoi(val);

	    //&&& AMW 7.8.98 - generic setting for health
		else if (!Q_strcasecmp(key, "health"))
			ent->health = atoi(val);

	    //&&& AMW 7.14.98 - generic setting for mass
		else if (!Q_strcasecmp(key, "mass"))
			ent->mass = atof(val);

		//	Nelno:	don't fill in sky any longer?
/*
		else if (!Q_strcasecmp(key, "sky"))
		{
			strcpy(sv.skyname, val);
		}
*/
	}
}

///////////////////////////////////////////////////////////////////////////////
//
///////////////////////////////////////////////////////////////////////////////

#define	SPAWNFLAG_NOT_COOP			0x00001000
typedef	(*func_t)(edict_t *);

void SpawnEntities (char *mapname, char *str, char *spawnpoint)
{
	edict_t		*ent;
	char		*com_token;
	func_t		func;
	int			entity_count = 0;
	int			removed = 0;

	ent = NULL;
	// serverState.time = sv.time;

	// see if we need to load up a file indicating which
	// entity not to load.  This is for nelno debugging only.
	// LoadIgnoreEntityList();

    // Sys_Log(str);

	memset (level.mapname, 0x00, sizeof (level.mapname));
	strncpy (level.mapname, mapname, sizeof(level.mapname)-1);

	// parse entites 
	for (;;)
	{
		com_token = COM_Parse (&str);
		if (!str)
			break;
		if (com_token [0] != '{')
			gi.Error ("SpawnEntities: found %s when expecting {",com_token);
/*
		if ((com_token = COM_Parse(&str)) == NULL) break;

		if (com_token[0] != '{')
		{
			//	Nelno: don't puke, please
			gi.error ("Sent_LoadServerEntities: found %s when expecting {",com_token);
		}
*/
		if (!ent) 
			ent = g_edicts;
		else
			ent = P_SpawnEdict ();

		entity_count++;

		// turn {} into a epair string
		str = Sent_EpairEdict (str, ent);

		// set classname
		ent->className = Sent_GetValue(ent->epair, "classname");

        if (!ent->className)
		{
			gi.Error ("Entity %i has no classname\n", entity_count);

			P_FreeEdict (ent);

			continue;
		}

		// fill in basic values
		Sent_BasicEpair(ent);

		// find spawn function for this class
		func = (func_t) DLL_FindFunction(ent->className);

		if (func == NULL)
		{
			// if this is the worldspawn classname then this
			// is a fatal error, otherwise we can let it slide
			if (!stricmp(ent->className, "worldspawn"))
				gi.Error ("Worldspawn entity MUST have a spawn function.");
			else
				gi.dprintf ("No spawn function found for %s.\n", ent->className);
		}

		//	Nelno:	ignore entity list
/*
		else if (IgnoreEntity(ent->className))
		{
			Con_Printf("Ignoring entity %s\n", ent->v.className);
		}
*/

		else
		{
			if (ent != g_edicts)
			{
				if (deathmatch->value)
				{
					//	don't spawn entities in deathmatch that shouldn't be spawned
					if (ent->spawnflags & SPAWNFLAG_NOT_DEATHMATCH || (ent->className && strstr (ent->className, "monster_") == ent->className))
					{
						P_FreeEdict (ent);	
						removed++;

						continue;
					}
				}
				else
				{
					if ( ((skill->value <= 25) && (ent->spawnflags & SPAWNFLAG_NOT_EASY)) ||
						 ((skill->value <= 50) && (ent->spawnflags & SPAWNFLAG_NOT_MEDIUM)) ||
						 (((skill->value <= 75) || (skill->value <= 100)) && (ent->spawnflags & SPAWNFLAG_NOT_HARD))
						)
						{
							P_FreeEdict (ent);	
							removed++;

							continue;
						}
				}

				ent->spawnflags &= ~(SPAWNFLAG_NOT_EASY | SPAWNFLAG_NOT_MEDIUM | SPAWNFLAG_NOT_HARD | SPAWNFLAG_NOT_COOP | SPAWNFLAG_NOT_DEATHMATCH);
			}

			// call spawn function
			func (ent);

			//	if this is a MOVETYPE_TOSS, MOVETYPE_BOUNCE, MOVETYPE_BOUNCEMISSILE or MOVETYPE_FLY
			//	add an epsilon to it so that it won't clip into the world if it was placed directly
			//	on the floor

#define	DIST_EPSILON	(0.03125)            // MIKE: didn't compile without this, so, I added it...
                                             // don't know if it's the correct #define (there were several
                                             // _EPSILON #defs) but, it compiles...

			if (ent->movetype == MOVETYPE_TOSS || ent->movetype == MOVETYPE_BOUNCE || 
				ent->movetype == MOVETYPE_BOUNCEMISSILE || ent->movetype == MOVETYPE_FLY ||
				ent->movetype == MOVETYPE_FLOAT || ent->movetype == MOVETYPE_WALK)
				ent->s.origin [2] += DIST_EPSILON;

		}
	}
}
