/*
===========================================================================

Doom 3 GPL Source Code
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.

This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code").

Doom 3 Source Code is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Doom 3 Source Code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Doom 3 Source Code.  If not, see <http://www.gnu.org/licenses/>.

In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code.  If not, please request a copy in writing from id Software at the address below.

If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.

===========================================================================
*/

#include "tools/edit_gui_common.h"


#include "qe3.h"
#include "../../renderer/tr_local.h"
#include "../../renderer/model_local.h"	// for idRenderModelMD5
int g_entityId = 1;

#define CURVE_TAG "curve_"

extern void Brush_Resize(brush_t *b, idVec3 vMin, idVec3 vMax);

int	GetNumKeys(entity_t *ent)
{
//	int iCount = 0;
//	for (epair_t* ep=ent->epairs ; ep ; ep=ep->next)
//	{
//		iCount++;
//	}

	int iCount = ent->epairs.GetNumKeyVals();
	return iCount;
}

const char *GetKeyString(entity_t *ent, int iIndex)
{
//	for (epair_t* ep=ent->epairs ; ep ; ep=ep->next)
//	{
//		if (!iIndex--)
//			return ep->key;
//	}
//
//	assert(0);
//	return NULL;

	if ( iIndex < GetNumKeys(ent) )
	{
		return ent->epairs.GetKeyVal(iIndex)->GetKey().c_str();
	}

	assert(0);
	return NULL;
}


/*
 =======================================================================================================================
 =======================================================================================================================
 */
const char *ValueForKey(entity_t *ent, const char *key) {
	return ent->epairs.GetString(key);
}

/*
 =======================================================================================================================
 =======================================================================================================================
 */
void TrackMD3Angles(entity_t *e, const char *key, const char *value) {
	if ( idStr::Icmp(key, "angle") != 0 ) {
		return;
	}

	if ((e->eclass->fixedsize && e->eclass->nShowFlags & ECLASS_MISCMODEL) || EntityHasModel(e)) {
		float	a = FloatForKey(e, "angle");
		float	b = atof(value);
		if (a != b) {
			idVec3	vAngle;
			vAngle[0] = vAngle[1] = 0;
			vAngle[2] = -a;
			Brush_Rotate(e->brushes.onext, vAngle, e->origin, true);
			vAngle[2] = b;
			Brush_Rotate(e->brushes.onext, vAngle, e->origin, true);
		}
	}
}

/*
 =======================================================================================================================
 =======================================================================================================================
 */
void SetKeyValue(entity_t *ent, const char *key, const char *value, bool trackAngles) {
	if (ent == NULL) {
		return;
	}

	if (!key || !key[0]) {
		return;
	}

	if (trackAngles) {
		TrackMD3Angles(ent, key, value);
	}

	ent->epairs.Set(key, value);
	GetVectorForKey(ent, "origin", ent->origin);

	// update sound in case this key was relevent
	Entity_UpdateSoundEmitter( ent );
}

/*
 =======================================================================================================================
 =======================================================================================================================
 */
void SetKeyVec3(entity_t *ent, const char *key, idVec3 v) {
	if (ent == NULL) {
		return;
	}

	if (!key || !key[0]) {
		return;
	}

	idStr str;
	sprintf(str, "%g %g %g", v.x, v.y, v.z);
	ent->epairs.Set(key, str);
	GetVectorForKey(ent, "origin", ent->origin);
}

/*
 =======================================================================================================================
 =======================================================================================================================
 */
void SetKeyMat3(entity_t *ent, const char *key, idMat3 m) {
	if (ent == NULL) {
		return;
	}

	if (!key || !key[0]) {
		return;
	}

	idStr str;

	sprintf(str, "%g %g %g %g %g %g %g %g %g",m[0][0],m[0][1],m[0][2],m[1][0],m[1][1],m[1][2],m[2][0],m[2][1],m[2][2]);

	ent->epairs.Set(key, str);
	GetVectorForKey(ent, "origin", ent->origin);
}



/*
 =======================================================================================================================
 =======================================================================================================================
 */
void DeleteKey(entity_t *ent, const char *key) {
	ent->epairs.Delete(key);
	if (stricmp(key, "rotation") == 0) {
		ent->rotation.Identity();
	}
}

/*
 =======================================================================================================================
 =======================================================================================================================
 */
float FloatForKey(entity_t *ent, const char *key) {
	const char	*k;

	k = ValueForKey(ent, key);
	if (k && *k) {
		return atof(k);
	}

	return 0.0;
}

/*
 =======================================================================================================================
 =======================================================================================================================
 */
int IntForKey(entity_t *ent, const char *key) {
	const char	*k;

	k = ValueForKey(ent, key);
	return atoi(k);
}

/*
 =======================================================================================================================
 =======================================================================================================================
 */
bool GetVectorForKey(entity_t *ent, const char *key, idVec3 &vec) {
	const char	*k;
	k = ValueForKey(ent, key);
	if (k && strlen(k) > 0) {
		sscanf(k, "%f %f %f", &vec[0], &vec[1], &vec[2]);
		return true;
	}
	else {
		vec[0] = vec[1] = vec[2] = 0;
	}

	return false;
}

/*
 =======================================================================================================================
 =======================================================================================================================
 */
bool GetVector4ForKey(entity_t *ent, const char *key, idVec4 &vec) {
	const char	*k;
	k = ValueForKey(ent, key);
	if (k && strlen(k) > 0) {
		sscanf(k, "%f %f %f %f", &vec[0], &vec[1], &vec[2], &vec[3]);
		return true;
	}
	else {
		vec[0] = vec[1] = vec[2] = vec[3] = 0;
	}

	return false;
}

/*
 =======================================================================================================================
 =======================================================================================================================
 */
bool GetFloatForKey(entity_t *ent, const char *key, float *f) {
	const char	*k;
	k = ValueForKey(ent, key);
	if (k && strlen(k) > 0) {
		*f = atof(k);
		return true;
	}

	*f = 0;
	return false;
}

/*
 =======================================================================================================================
 =======================================================================================================================
 */
bool GetMatrixForKey(entity_t *ent, const char *key, idMat3 &mat) {
	const char	*k;
	k = ValueForKey(ent, key);
	if (k && strlen(k) > 0) {
		sscanf
		(
			k,
			"%f %f %f %f %f %f %f %f %f ",
			&mat[0][0],
			&mat[0][1],
			&mat[0][2],
			&mat[1][0],
			&mat[1][1],
			&mat[1][2],
			&mat[2][0],
			&mat[2][1],
			&mat[2][2]
		);
		return true;
	}
	else {
		mat.Identity();
	}

	return false;
}

/*
 =======================================================================================================================
	Entity_FreeEpairs Frees the entity epairs.
 =======================================================================================================================
 */
void Entity_FreeEpairs(entity_t *e) {
	e->epairs.Clear();
}

/*
 =======================================================================================================================
	Entity_AddToList
 =======================================================================================================================
 */
void Entity_AddToList(entity_t *e, entity_t *list) {
	if (e->next || e->prev) {
		Error("Entity_AddToList: already linked");
	}

	e->next = list->next;
	list->next->prev = e;
	list->next = e;
	e->prev = list;
}

/*
 =======================================================================================================================
	Entity_RemoveFromList
 =======================================================================================================================
 */
void Entity_RemoveFromList(entity_t *e) {
	if ( !e->next || !e->prev ) {
		Error("Entity_RemoveFromList: not linked");
	}

	e->next->prev = e->prev;
	e->prev->next = e->next;
	e->next = e->prev = NULL;
}

/*
 =======================================================================================================================
	Entity_Free Frees the entity and any brushes is has. The entity is removed from the global entities list.
 =======================================================================================================================
 */
void Entity_Free( entity_t *e ) {

	while ( e->brushes.onext != &e->brushes ) {
		Brush_Free(e->brushes.onext);
	}

	if ( e->next ) {
		e->next->prev = e->prev;
		e->prev->next = e->next;
	}

	Entity_FreeEpairs( e );

	delete e;
}

/*
 =======================================================================================================================
	Entity_MemorySize
 =======================================================================================================================
 */

int Entity_MemorySize( entity_t *e )
{
	brush_t		*b;
	int			size;

	size = sizeof( entity_t ) + e->epairs.Size();
	for( b = e->brushes.onext; b != &e->brushes; b = b->onext )
	{
		size += Brush_MemorySize( b );
}
	return( size );
}

/*
 =======================================================================================================================
	ParseEpair
 =======================================================================================================================
 */

struct EpairFixup {
	const char *name;
	int type;
};


const EpairFixup FloatFixups[] = {
	{ "origin", 1 },
	{ "rotation", 2 },
	{ "_color", 1 },
	{ "falloff", 0 },
	{ "light", 0 },
	{ "light_target", 1 },
	{ "light_up", 1 },
	{ "light_right", 1 },
	{ "light_start", 1 },
	{ "light_center", 1 },
	{ "light_end", 1 },
	{ "light_radius", 1 },
	{ "light_origin", 1 }
};

const int FixupCount = sizeof(FloatFixups) / sizeof(EpairFixup);

void FixFloats(idDict *dict) {
	int count = dict->GetNumKeyVals();
	for (int i = 0; i < count; i++) {
		const idKeyValue *kv = dict->GetKeyVal(i);
		for (int j = 0; j < FixupCount; j++) {
			if (kv->GetKey().Icmp(FloatFixups[j].name) == 0) {
				idStr val;
				if (FloatFixups[j].type == 1) {
					idVec3 v;
					sscanf(kv->GetValue().c_str(), "%f %f %f", &v.x, &v.y, &v.z);
					sprintf(val, "%g %g %g", v.x, v.y, v.z);
				} else if (FloatFixups[j].type == 2) {
					idMat3 mat;
					sscanf(kv->GetValue().c_str(),	"%f %f %f %f %f %f %f %f %f ",&mat[0][0],&mat[0][1],&mat[0][2],&mat[1][0],&mat[1][1],&mat[1][2],&mat[2][0],&mat[2][1],&mat[2][2]);
					sprintf(val, "%g %g %g %g %g %g %g %g %g",mat[0][0],mat[0][1],mat[0][2],mat[1][0],mat[1][1],mat[1][2],mat[2][0],mat[2][1],mat[2][2]);
				} else {
					float f = atof(kv->GetValue().c_str());
					sprintf(val, "%g", f);
				}
				dict->Set(kv->GetKey(), val);
				break;
			}
		}
	}
}

void ParseEpair(idDict *dict) {
	idStr key = token;
	GetToken(false);
	idStr val = token;

	if (key.Length() > 0) {
		dict->Set(key, val);
	}
}

/*
 =======================================================================================================================
 =======================================================================================================================
 */
bool EntityHasModel(entity_t *ent) {
	if (ent) {
		const char	*model = ValueForKey(ent, "model");
		const char	*name = ValueForKey(ent, "name");
		if (model && *model) {
			if ( idStr::Icmp(model, name) ) {
				return true;
			}
		}
	}

	return false;
}

/*
 =======================================================================================================================
 =======================================================================================================================
 */
entity_t *Entity_New() {
	entity_t *ent = new entity_t;

	ent->prev = ent->next = NULL;
	ent->brushes.prev = ent->brushes.next = NULL;
	ent->brushes.oprev = ent->brushes.onext = NULL;
	ent->brushes.owner = NULL;
	ent->undoId = 0;
	ent->redoId = 0;
	ent->entityId = g_entityId++;
	ent->origin.Zero();
	ent->eclass = NULL;
	ent->md3Class = NULL;
	ent->lightOrigin.Zero();
	ent->lightRotation.Identity();
	ent->trackLightOrigin = false;
	ent->rotation.Identity();
	ent->lightDef = -1;
	ent->modelDef = -1;
	ent->soundEmitter = NULL;
	ent->curve = NULL;
	return ent;
}

void Entity_UpdateCurveData( entity_t *ent ) {

	if ( ent == NULL || ent->curve == NULL ) {
		return;
	}

	const idKeyValue *kv = ent->epairs.MatchPrefix( CURVE_TAG );
	if ( kv == NULL ) {
		if ( ent->curve ) {
			delete ent->curve;
			ent->curve = NULL;
			if ( g_qeglobals.d_select_mode == sel_editpoint ) {
				g_qeglobals.d_select_mode = sel_brush;
			}
		}
		return;
	}

	int c = ent->curve->GetNumValues();
	idStr str = va( "%i ( ", c );
	idVec3 v;
	for ( int i = 0; i < c; i++ ) {
		v = ent->curve->GetValue( i );
		str += " ";
		str += v.ToString();
		str += " ";
	}
	str += " )";

	ent->epairs.Set( kv->GetKey(), str );

}

idCurve<idVec3> *Entity_MakeCurve( entity_t *ent ) {
	const idKeyValue *kv = ent->epairs.MatchPrefix( CURVE_TAG );
	if ( kv ) {
		idStr str = kv->GetKey().Right( kv->GetKey().Length() - strlen( CURVE_TAG ) );
		if ( str.Icmp( "CatmullRomSpline" ) == 0 ) {
			return new idCurve_CatmullRomSpline<idVec3>();
		} else if ( str.Icmp( "Nurbs" ) == 0 ) {
			return new idCurve_NURBS<idVec3>();
		}
	}
	return NULL;
}

void Entity_SetCurveData( entity_t *ent ) {

	ent->curve = Entity_MakeCurve( ent );
	const idKeyValue *kv = ent->epairs.MatchPrefix( CURVE_TAG );
	if ( kv && ent->curve ) {
		idLexer lex;
		lex.LoadMemory( kv->GetValue(), kv->GetValue().Length(), "_curve" );
		int numPoints = lex.ParseInt();
		if ( numPoints > 0 ) {
			float *fp = new float[numPoints * 3];
			lex.Parse1DMatrix( numPoints * 3, fp );
			int time = 0;
			for ( int i = 0; i < numPoints * 3; i += 3 ) {
				idVec3 v;
				v.x = fp[i];
				v.y = fp[i+1];
				v.z = fp[i+2];
				ent->curve->AddValue( time, v );
				time += 100;
			}
			delete []fp;
		}
	}

}

entity_t *Entity_PostParse(entity_t *ent, brush_t *pList) {
	bool		has_brushes;
	eclass_t	*e;
	brush_t		*b;
	idVec3		mins, maxs, zero;
	idBounds bo;

	zero.Zero();

	Entity_SetCurveData( ent );

	if (ent->brushes.onext == &ent->brushes) {
		has_brushes = false;
	}
	else {
		has_brushes = true;
	}

	bool needsOrigin = !GetVectorForKey(ent, "origin", ent->origin);
	const char	*pModel = ValueForKey(ent, "model");

	const char *cp = ValueForKey(ent, "classname");

	if (strlen(cp)) {
		e = Eclass_ForName(cp, has_brushes);
	} else {
		const char *cp2 = ValueForKey(ent, "name");
		if (strlen(cp2)) {
			char buff[1024];
			strcpy(buff, cp2);
			int len = strlen(buff);
			while ((isdigit(buff[len-1]) || buff[len-1] == '_') && len > 0) {
				buff[len-1] = '\0';
				len--;
			}
			e = Eclass_ForName(buff, has_brushes);
			SetKeyValue(ent, "classname", buff, false);
		} else {
			e = Eclass_ForName("", has_brushes);
		}
	}

	idStr str;

	if (e->defArgs.GetString("model", "", str) && e->entityModel == NULL) {
		e->entityModel = gameEdit->ANIM_GetModelFromEntityDef( &e->defArgs );
	}

	ent->eclass = e;

	bool hasModel = EntityHasModel(ent);

	if (hasModel) {
		ent->eclass->defArgs.GetString("model", "", str);
		if (str.Length()) {
			hasModel = false;
			ent->epairs.Delete("model");
		}
	}

	if (e->nShowFlags & ECLASS_WORLDSPAWN) {
		// DG: this makes sure that tr.allowNoSpecular is set appropriately when loading a map
		const char* noSpecVal = ValueForKey(ent, "allow_nospecular");
		tr.allowNoSpecular = noSpecVal && *noSpecVal && atoi(noSpecVal) != 0;
		common->Printf("This map does%s support 'nospecular' lights\n", tr.allowNoSpecular ? "" : " not");

		ent->origin.Zero();
		needsOrigin = false;
		ent->epairs.Delete( "model" );
	} else if (e->nShowFlags & ECLASS_LIGHT) {
		if (GetVectorForKey(ent, "light_origin", ent->lightOrigin)) {
			GetMatrixForKey(ent, "light_rotation", ent->lightRotation);
			ent->trackLightOrigin = true;
		} else if (hasModel) {
			SetKeyValue(ent, "light_origin", ValueForKey(ent, "origin"));
			ent->lightOrigin = ent->origin;
			if (GetMatrixForKey(ent, "rotation", ent->lightRotation)) {
				SetKeyValue(ent, "light_rotation", ValueForKey(ent, "rotation"));
			}
			ent->trackLightOrigin = true;
		}
	} else if ( e->nShowFlags & ECLASS_ENV ) {
		// need to create an origin from the bones here
		idVec3 org;
		idAngles ang;
		bo.Clear();
		bool hasBody = false;
		const idKeyValue *arg = ent->epairs.MatchPrefix( "body ", NULL );
		while ( arg ) {
			sscanf( arg->GetValue(), "%f %f %f %f %f %f", &org.x, &org.y, &org.z, &ang.pitch, &ang.yaw, &ang.roll );
			bo.AddPoint( org );
			arg = ent->epairs.MatchPrefix( "body ", arg );
			hasBody = true;
		}
		if (hasBody) {
			ent->origin = bo.GetCenter();
		}
	}

	if (e->fixedsize || hasModel) {			// fixed size entity
		if (ent->brushes.onext != &ent->brushes) {
			for (b = ent->brushes.onext; b != &ent->brushes; b = b->onext) {
				b->entityModel = true;
			}
		}

		if (hasModel) {
			// model entity
			idRenderModel *modelHandle = renderModelManager->FindModel( pModel );

			if ( dynamic_cast<idRenderModelPrt*>( modelHandle ) || dynamic_cast<idRenderModelLiquid*>( modelHandle ) ) {
				bo.Zero();
				bo.ExpandSelf( 12.0f );
			} else {
				bo = modelHandle->Bounds( NULL );
			}

			VectorCopy(bo[0], mins);
			VectorCopy(bo[1], maxs);
			for (int i = 0; i < 3; i++) {
				if (mins[i] == maxs[i]) {
					mins[i]--;
					maxs[i]++;
				}
			}
			VectorAdd(mins, ent->origin, mins);
			VectorAdd(maxs, ent->origin, maxs);
			b = Brush_Create(mins, maxs, &e->texdef);
			b->modelHandle = modelHandle;

			float		yaw = 0;
			bool		convertAngles = GetFloatForKey(ent, "angle", &yaw);
			extern void Brush_Rotate(brush_t *b, idMat3 matrix, idVec3 origin, bool bBuild);
			extern void Brush_Rotate(brush_t *b, idVec3 rot, idVec3 origin, bool bBuild);

			if (convertAngles) {
				idVec3	rot(0, 0, yaw);
				Brush_Rotate(b, rot, ent->origin, false);
			}

			if (GetMatrixForKey(ent, "rotation", ent->rotation)) {
				idBounds bo2;
				bo2.FromTransformedBounds(bo, ent->origin, ent->rotation);
				b->owner = ent;
				Brush_Resize(b, bo2[0], bo2[1]);
			}
			Entity_LinkBrush(ent, b);
		}

		if (!hasModel || (ent->eclass->nShowFlags & ECLASS_LIGHT && hasModel)) {
			// create a custom brush
			if (ent->trackLightOrigin) {
				mins = e->mins + ent->lightOrigin;
				maxs = e->maxs + ent->lightOrigin;
			} else {
				mins = e->mins + ent->origin;
				maxs = e->maxs + ent->origin;
			}

			b = Brush_Create(mins, maxs, &e->texdef);
			GetMatrixForKey(ent, "rotation", ent->rotation);
			Entity_LinkBrush(ent, b);
			b->trackLightOrigin = ent->trackLightOrigin;
			if ( e->texdef.name == NULL ) {
				brushprimit_texdef_t bp;
				texdef_t td;
				td.SetName( ent->eclass->defMaterial );
				Brush_SetTexture( b, &td, &bp, false );
			}
		}
	} else {	// brush entity
		if (ent->brushes.next == &ent->brushes) {
			printf("Warning: Brush entity with no brushes\n");
		}

		if (!needsOrigin) {
			idStr cn = ValueForKey(ent, "classname");
			idStr name = ValueForKey(ent, "name");
			idStr model = ValueForKey(ent, "model");
			if (cn.Icmp("func_static") == 0) {
				if (name.Icmp(model) == 0) {
					needsOrigin = true;
				}
			}
		}

		if (needsOrigin) {
			idVec3	entmins, entmaxs, mid;
			int		i;
			char	text[32];
			entmins[0] = entmins[1] = entmins[2] = 999999;
			entmaxs[0] = entmaxs[1] = entmaxs[2] = -999999;

			// add in the origin
			for (b = ent->brushes.onext; b != &ent->brushes; b = b->onext) {
				Brush_Build(b, true, false, false);
				for (i = 0; i < 3; i++) {
					if (b->mins[i] < entmins[i]) {
						entmins[i] = b->mins[i];
					}

					if (b->maxs[i] > entmaxs[i]) {
						entmaxs[i] = b->maxs[i];
					}
				}
			}

			for (i = 0; i < 3; i++) {
				ent->origin[i] = (entmins[i] + ((entmaxs[i] - entmins[i]) / 2));
			}

			sprintf(text, "%i %i %i", (int)ent->origin[0], (int)ent->origin[1], (int)ent->origin[2]);
			SetKeyValue(ent, "origin", text);
		}

		if (!(e->nShowFlags & ECLASS_WORLDSPAWN)) {
			if (e->defArgs.FindKey("model") == NULL && (pModel == NULL || (pModel && strlen(pModel) == 0))) {
				SetKeyValue(ent, "model", ValueForKey(ent, "name"));
			}
		}
		else {
			DeleteKey(ent, "origin");
		}
	}

	// add all the brushes to the main list
	if (pList) {
		for (b = ent->brushes.onext; b != &ent->brushes; b = b->onext) {
			b->next = pList->next;
			pList->next->prev = b;
			b->prev = pList;
			pList->next = b;
		}
	}

	FixFloats(&ent->epairs);

	return ent;

}

/*
 =======================================================================================================================
	Entity_Parse If onlypairs is set, the classname info will not be looked up, and the entity will not be added to the
	global list. Used for parsing the project.
 =======================================================================================================================
 */
entity_t *Entity_Parse(bool onlypairs, brush_t *pList) {
	entity_t	*ent;

	if (!GetToken(true)) {
		return NULL;
	}

	if (strcmp(token, "{")) {
		Error("ParseEntity: { not found");
	}

	ent = Entity_New();
	ent->brushes.onext = ent->brushes.oprev = &ent->brushes;
	ent->origin.Zero();

	do {
		if (!GetToken(true)) {
			Warning("ParseEntity: EOF without closing brace");
			return NULL;
		}

		if (!strcmp(token, "}")) {
			break;
		}

		if (!strcmp(token, "{")) {
			GetVectorForKey(ent, "origin", ent->origin);
			brush_t *b = Brush_Parse(ent->origin);
			if (b != NULL) {
				b->owner = ent;

				// add to the end of the entity chain
				b->onext = &ent->brushes;
				b->oprev = ent->brushes.oprev;
				ent->brushes.oprev->onext = b;
				ent->brushes.oprev = b;
			}
			else {
				break;
			}
		}
		else {
			ParseEpair(&ent->epairs);
		}
	} while (1);

	if (onlypairs) {
		return ent;
	}

	return Entity_PostParse(ent, pList);
}

/*
 =======================================================================================================================
 =======================================================================================================================
 */
void VectorMidpoint(idVec3 va, idVec3 vb, idVec3 &out) {
	for (int i = 0; i < 3; i++) {
		out[i] = va[i] + ((vb[i] - va[i]) / 2);
	}
}

/*
 =======================================================================================================================
	Entity_Write
 =======================================================================================================================
 */
void Entity_Write(entity_t *e, FILE *f, bool use_region) {
	brush_t *b;
	idVec3	origin;
	char	text[128];
	int		count;

	// if none of the entities brushes are in the region, don't write the entity at all
	if (use_region) {
		// in region mode, save the camera position as playerstart
		if (!strcmp(ValueForKey(e, "classname"), "info_player_start")) {
			fprintf(f, "{\n");
			fprintf(f, "\"classname\" \"info_player_start\"\n");
			fprintf
			(
				f,
				"\"origin\" \"%i %i %i\"\n",
				(int)g_pParentWnd->GetCamera()->Camera().origin[0],
				(int)g_pParentWnd->GetCamera()->Camera().origin[1],
				(int)g_pParentWnd->GetCamera()->Camera().origin[2]
			);
			fprintf(f, "\"angle\" \"%i\"\n", (int)g_pParentWnd->GetCamera()->Camera().angles[YAW]);
			fprintf(f, "}\n");
			return;
		}

		for (b = e->brushes.onext; b != &e->brushes; b = b->onext) {
			if (!Map_IsBrushFiltered(b)) {
				break;	// got one
			}
		}

		if (b == &e->brushes) {
			return;		// nothing visible
		}
	}

	if (e->eclass->nShowFlags & ECLASS_PLUGINENTITY) {
		// NOTE: the whole brush placement / origin stuff is a mess
		VectorCopy(e->origin, origin);
		sprintf(text, "%i %i %i", (int)origin[0], (int)origin[1], (int)origin[2]);
		SetKeyValue(e, "origin", text);
	}

	// if fixedsize, calculate a new origin based on the current brush position
	else if (e->eclass->fixedsize || EntityHasModel(e)) {
		if (!GetVectorForKey(e, "origin", origin)) {
			VectorSubtract(e->brushes.onext->mins, e->eclass->mins, origin);
			sprintf(text, "%i %i %i", (int)origin[0], (int)origin[1], (int)origin[2]);
			SetKeyValue(e, "origin", text);
		}
	}

	fprintf(f, "{\n");

	count = e->epairs.GetNumKeyVals();
	for (int j = 0; j < count; j++) {
		fprintf(f, "\"%s\" \"%s\"\n", e->epairs.GetKeyVal(j)->GetKey().c_str(), e->epairs.GetKeyVal(j)->GetValue().c_str());
	}

	if (!EntityHasModel(e)) {
		count = 0;
		for (b = e->brushes.onext; b != &e->brushes; b = b->onext) {
			if (e->eclass->fixedsize && !b->entityModel) {
				continue;
			}
			if (!use_region || !Map_IsBrushFiltered(b)) {
				fprintf(f, "// brush %i\n", count);
				count++;
				Brush_Write( b, f, e->origin, ( g_PrefsDlg.m_bNewMapFormat != FALSE ) );
			}
		}
	}

	fprintf(f, "}\n");
}

/*
 =======================================================================================================================
 =======================================================================================================================
 */
bool IsBrushSelected(brush_t *bSel) {
	for (brush_t * b = selected_brushes.next; b != NULL && b != &selected_brushes; b = b->next) {
		if (b == bSel) {
			return true;
		}
	}

	return false;
}

//
// =======================================================================================================================
//    Entity_WriteSelected
// =======================================================================================================================
//
void Entity_WriteSelected(entity_t *e, FILE *f) {
	brush_t *b;
	idVec3	origin;
	char	text[128];
	int		count;

	for (b = e->brushes.onext; b != &e->brushes; b = b->onext) {
		if (IsBrushSelected(b)) {
			break;	// got one
		}
	}

	if (b == &e->brushes) {
		return;		// nothing selected
	}

	// if fixedsize, calculate a new origin based on the current brush position
	if (e->eclass->fixedsize || EntityHasModel(e)) {
		if (!GetVectorForKey(e, "origin", origin)) {
			VectorSubtract(e->brushes.onext->mins, e->eclass->mins, origin);
			sprintf(text, "%i %i %i", (int)origin[0], (int)origin[1], (int)origin[2]);
			SetKeyValue(e, "origin", text);
		}
	}

	fprintf(f, "{\n");

	count = e->epairs.GetNumKeyVals();
	for (int j = 0; j < count; j++) {
		fprintf(f, "\"%s\" \"%s\"\n", e->epairs.GetKeyVal(j)->GetKey().c_str(), e->epairs.GetKeyVal(j)->GetValue().c_str());
	}

	if (!EntityHasModel(e)) {
		count = 0;
		for (b = e->brushes.onext; b != &e->brushes; b = b->onext) {
			if (e->eclass->fixedsize && !b->entityModel) {
				continue;
			}
			if (IsBrushSelected(b)) {
				fprintf(f, "// brush %i\n", count);
				count++;
				Brush_Write( b, f, e->origin, ( g_PrefsDlg.m_bNewMapFormat != FALSE ) );
			}
		}
	}

	fprintf(f, "}\n");
}

//
// =======================================================================================================================
//    Entity_WriteSelected to a CMemFile
// =======================================================================================================================
//
void Entity_WriteSelected(entity_t *e, CMemFile *pMemFile) {
	brush_t *b;
	idVec3	origin;
	char	text[128];
	int		count;

	for (b = e->brushes.onext; b != &e->brushes; b = b->onext) {
		if (IsBrushSelected(b)) {
			break;	// got one
		}
	}

	if (b == &e->brushes) {
		return;		// nothing selected
	}

	// if fixedsize, calculate a new origin based on the current brush position
	if (e->eclass->fixedsize || EntityHasModel(e)) {
		if (!GetVectorForKey(e, "origin", origin)) {
			VectorSubtract(e->brushes.onext->mins, e->eclass->mins, origin);
			sprintf(text, "%i %i %i", (int)origin[0], (int)origin[1], (int)origin[2]);
			SetKeyValue(e, "origin", text);
		}
	}

	MemFile_fprintf(pMemFile, "{\n");

	count = e->epairs.GetNumKeyVals();
	for (int j = 0; j < count; j++) {
		MemFile_fprintf(pMemFile, "\"%s\" \"%s\"\n", e->epairs.GetKeyVal(j)->GetKey().c_str(), e->epairs.GetKeyVal(j)->GetValue().c_str());
	}

	if (!EntityHasModel(e)) {
		count = 0;
		for (b = e->brushes.onext; b != &e->brushes; b = b->onext) {
			if (e->eclass->fixedsize && !b->entityModel) {
				continue;
			}
			if (IsBrushSelected(b)) {
				MemFile_fprintf(pMemFile, "// brush %i\n", count);
				count++;
				Brush_Write( b, pMemFile, e->origin, ( g_PrefsDlg.m_bNewMapFormat != FALSE ) );
			}
		}
	}

	MemFile_fprintf(pMemFile, "}\n");
}

/*
 =======================================================================================================================
 =======================================================================================================================
 */
void Entity_SetName(entity_t *e, const char *name) {
	CString oldName = ValueForKey(e, "name");
	CString oldModel = ValueForKey(e, "model");
	SetKeyValue(e, "name", name);
	if (oldName == oldModel) {
		SetKeyValue(e, "model", name);
	}
}

extern bool Entity_NameIsUnique(const char *name);

/*
 =======================================================================================================================
 =======================================================================================================================
 */
void Entity_Name(entity_t *e, bool force) {
	const char	*name = ValueForKey(e, "name");

	if (!force && name && name[0]) {
		return;
	}

	if (name && name[0] && Entity_NameIsUnique(name)) {
		return;
	}

	bool	setModel = false;
	if (name[0]) {
		const char	*model = ValueForKey(e, "model");
		if (model[0]) {
			if ( idStr::Icmp(model, name) == 0 ) {
				setModel = true;
			}
		}
	}

	const char *eclass = ValueForKey(e, "classname");
	if (eclass && eclass[0]) {
		idStr str = cvarSystem->GetCVarString( "radiant_nameprefix" );
		int id = Map_GetUniqueEntityID(str, eclass);
		if (str.Length()) {
			SetKeyValue(e, "name", va("%s_%s_%i", str.c_str(), eclass, id));
		} else {
			SetKeyValue(e, "name", va("%s_%i", eclass, id));
		}
		if (setModel) {
			if (str.Length()) {
				SetKeyValue(e, "model", va("%s_%s_%i", str.c_str(), eclass, id));
			} else {
				SetKeyValue(e, "model", va("%s_%i",  eclass, id));
			}
		}
	}
}

/*
 =======================================================================================================================
	Entity_Create Creates a new entity out of the selected_brushes list. If the entity class is fixed size, the brushes
	are only used to find a midpoint. Otherwise, the brushes have their ownership transfered to the new entity.
 =======================================================================================================================
 */
entity_t *Entity_Create(eclass_t *c, bool forceFixed) {
	entity_t	*e;
	brush_t		*b;
	idVec3		mins, maxs, origin;
	char		text[32];
	texdef_t td;
	brushprimit_texdef_t bp;

	// check to make sure the brushes are ok
	for (b = selected_brushes.next; b != &selected_brushes; b = b->next) {
		if (b->owner != world_entity) {
			Sys_Status("Entity NOT created, brushes not all from world\n");
			Sys_Beep();
			return NULL;
		}
	}

	idStr str;
	if (c->defArgs.GetString("model", "", str) && c->entityModel == NULL) {
		c->entityModel = gameEdit->ANIM_GetModelFromEntityDef( &c->defArgs );
	}

	// create it
	e = Entity_New();
	e->brushes.onext = e->brushes.oprev = &e->brushes;
	e->eclass = c;
	e->epairs.Copy(c->args);
	SetKeyValue(e, "classname", c->name);
	Entity_Name(e, false);

	// add the entity to the entity list
	Entity_AddToList(e, &entities);

	if (c->fixedsize) {
		//
		// just use the selection for positioning b = selected_brushes.next; for (i=0 ;
		// i<3 ; i++) { e->origin[i] = b->mins[i] - c->mins[i]; }
		//
		Select_GetMid(e->origin);
		VectorCopy(e->origin, origin);

		// create a custom brush
		VectorAdd(c->mins, e->origin, mins);
		VectorAdd(c->maxs, e->origin, maxs);

		b = Brush_Create(mins, maxs, &c->texdef);

		Entity_LinkBrush(e, b);

		if (c->defMaterial.Length()) {
			td.SetName(c->defMaterial);
			Brush_SetTexture(b, &td, &bp, false);
		}


		// delete the current selection
		Select_Delete();

		// select the new brush
		b->next = b->prev = &selected_brushes;
		selected_brushes.next = selected_brushes.prev = b;

		Brush_Build(b);
	}
	else {

		Select_GetMid(origin);

		// change the selected brushes over to the new entity
		for (b = selected_brushes.next; b != &selected_brushes; b = b->next) {
			Entity_UnlinkBrush(b);
			Entity_LinkBrush(e, b);
			Brush_Build(b); // so the key brush gets a name
			if (c->defMaterial.Length()) {
				td.SetName(c->defMaterial);
				Brush_SetTexture(b, &td, &bp, false);
			}

		}

		//for (int i = 0; i < 3; i++) {
		//	origin[i] = vMin[i] + vMax[i] * 0.5;
		//}

		if (!forceFixed) {
			SetKeyValue(e, "model", ValueForKey(e, "name"));
		}
	}

	sprintf(text, "%i %i %i", (int)origin[0], (int)origin[1], (int)origin[2]);
	SetKeyValue(e, "origin", text);
	VectorCopy(origin, e->origin);

	Sys_UpdateWindows(W_ALL);
	return e;
}

void Brush_MakeDirty(brush_t *b) {
	for (face_t *f = b->brush_faces; f; f = f->next) {
		f->dirty = true;
	}
}
/*
 =======================================================================================================================
	Entity_LinkBrush
 =======================================================================================================================
 */
void Entity_LinkBrush(entity_t *e, brush_t *b) {
	if (b->oprev || b->onext) {
		Error("Entity_LinkBrush: Allready linked");
	}

	Brush_MakeDirty(b);

	b->owner = e;

	b->onext = e->brushes.onext;
	b->oprev = &e->brushes;
	e->brushes.onext->oprev = b;
	e->brushes.onext = b;
}

/*
 =======================================================================================================================
	Entity_UnlinkBrush
 =======================================================================================================================
 */
void Entity_UnlinkBrush(brush_t *b) {
	// if (!b->owner || !b->onext || !b->oprev)
	if (!b->onext || !b->oprev) {
		Error("Entity_UnlinkBrush: Not currently linked");
	}

	b->onext->oprev = b->oprev;
	b->oprev->onext = b->onext;
	b->onext = b->oprev = NULL;
	b->owner = NULL;
}

/*
 =======================================================================================================================
	Entity_Clone
 =======================================================================================================================
 */
entity_t *Entity_Clone(entity_t *e) {
	entity_t	*n;

	n = Entity_New();
	n->brushes.onext = n->brushes.oprev = &n->brushes;
	n->eclass = e->eclass;
	n->rotation = e->rotation;
	n->origin = e->origin;

	// add the entity to the entity list
	Entity_AddToList(n, &entities);

	n->epairs  = e->epairs;

	return n;
}

/*
 =======================================================================================================================
 =======================================================================================================================
 */
int GetUniqueTargetId(int iHint) {
	int			iMin, iMax, i;
	BOOL		fFound;
	entity_t	*pe;

	fFound = FALSE;
	pe = entities.next;
	iMin = 0;
	iMax = 0;

	for (; pe != NULL && pe != &entities; pe = pe->next) {
		i = IntForKey(pe, "target");
		if (i) {
			iMin = Min(i, iMin);
			iMax = Max(i, iMax);
			if (i == iHint) {
				fFound = TRUE;
			}
		}
	}

	if (fFound) {
		return iMax + 1;
	}
	else {
		return iHint;
	}
}

/*
 =======================================================================================================================
 =======================================================================================================================
 */
entity_t *FindEntity(const char *pszKey, const char *pszValue) {
	entity_t	*pe;

	pe = entities.next;

	for (; pe != NULL && pe != &entities; pe = pe->next) {
		if (!strcmp(ValueForKey(pe, pszKey), pszValue)) {
			return pe;
		}
	}

	return NULL;
}

/*
 =======================================================================================================================
 =======================================================================================================================
 */
entity_t *FindEntityInt(const char *pszKey, int iValue) {
	entity_t	*pe;

	pe = entities.next;

	for (; pe != NULL && pe != &entities; pe = pe->next) {
		if (IntForKey(pe, pszKey) == iValue) {
			return pe;
		}
	}

	return NULL;
}

/*
====================
Entity_UpdateSoundEmitter

Deletes the soundEmitter if the entity should not emit a sound due
to it not having one, being filtered away, or the sound mode being turned off.

Creates or updates the soundEmitter if needed
====================
*/
void Entity_UpdateSoundEmitter( entity_t *ent ) {
	bool	playing = false;

	// if an entity doesn't have any brushes at all, don't do anything
	// if the brush isn't displayed (filtered or culled), don't do anything
	if ( g_pParentWnd->GetCamera()->GetSoundMode()
		&& ent->brushes.onext != &ent->brushes && !FilterBrush(ent->brushes.onext) ) {
		// check for sounds
		const char *v = ValueForKey( ent, "s_shader" );
		if ( v[0] ) {
			refSound_t	sound;

			gameEdit->ParseSpawnArgsToRefSound( &ent->epairs, &sound );
			if ( !sound.waitfortrigger ) {	// waitfortrigger will not start playing immediately
				if ( !ent->soundEmitter ) {
					ent->soundEmitter = g_qeglobals.sw->AllocSoundEmitter();
				}
				playing = true;
				ent->soundEmitter->UpdateEmitter( ent->origin, 0, &sound.parms );
				// always play on a single channel, so updates always override
				ent->soundEmitter->StartSound( sound.shader, SCHANNEL_ONE );
			}
		}
	}

	// delete the soundEmitter if not used
	if ( !playing && ent->soundEmitter ) {
		ent->soundEmitter->Free( true );
		ent->soundEmitter = NULL;
	}

}
