/*
Copyright (C) 1999-2007 id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.

This file is part of GtkRadiant.

GtkRadiant 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 2 of the License, or
(at your option) any later version.

GtkRadiant 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 GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/

#include "plugin.h"
#include "entity.h"
#include "entity_entitymodel.h"
#include "light.h"

int g_entityId = 1;



// internal

static void Entity_FreeEpairs(entity_t *e);

static void SetKeyValue (epair_t *&e, const char *key, const char *value);
static void  DeleteKey (epair_t *&e, const char *key);
static const char *ValueForKey ( epair_t *&e, const char *key);

static void Entity_OnKeyValueChanged(entity_t *e, const char* key, const char* value);

// constructor
entity_t *Entity_Alloc()
{
  entity_t *e;
	e = (entity_t*)malloc (sizeof(*e));
	e->entityId = g_entityId++;
  VectorSet(e->origin, 0, 0, 0);
  VectorSet(e->color, 1, 1, 1);
  e->redoId = 0;
  e->undoId = 0;
  e->next = e->prev = NULL;
	e->brushes.onext = e->brushes.oprev = &e->brushes;
  e->epairs = NULL;
  e->eclass = NULL;
  e->model.pRender = NULL;
  e->model.pSelect = NULL;
  e->model.pEdit = NULL;
  return e;
}

// destructor
void Entity_Free (entity_t *e)
{
	while (e->brushes.onext != &e->brushes)
		Brush_Free( e->brushes.onext, true );

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

	Entity_FreeEpairs(e);

  if (e->model.pRender)
  {
    e->model.pRender->DecRef();
    e->model.pRender = NULL;
  }
  if (e->model.pSelect)
  {
    e->model.pSelect->DecRef();
    e->model.pSelect = NULL;
  }
  if (e->model.pEdit)
  {
    e->model.pEdit->DecRef();
    e->model.pEdit = NULL;
  }

	free (e);
}

// construct from entity
entity_t	*Entity_Clone (entity_t *e)
{
	entity_t	*n;
	epair_t		*ep;

	n = Entity_Alloc();
	n->eclass = e->eclass;

	for (ep = e->epairs ; ep ; ep=ep->next)
    SetKeyValue(n, ep->key, ep->value);

	// copy some misc stuff as well
	VectorCopy( e->origin, n->origin );
//	VectorCopy( e->vRotation, n->vRotation );
//	VectorCopy( e->vScale, n->vScale );

//  n->bDirty = true;

	return n;
}





const char *ValueForKey ( epair_t *&e, const char *key)
{
  epair_t *ep;
  for (ep=e ; ep ; ep=ep->next)
  {
		if (!strcmp (ep->key, key) )
    {
      return ep->value;
    }
  }
  return "";
}

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

void 	SetKeyValue (epair_t *&e, const char *key, const char *value)
{
	epair_t	*ep;
  for (ep=e ; ep ; ep=ep->next)
  {
		if (!strcmp (ep->key, key) )
		{
			free (ep->value);
			ep->value = (char*)malloc(strlen(value)+1);
			strcpy (ep->value, value);
			return;
		}
  }
	ep = (epair_t*)malloc (sizeof(*ep));
	ep->next = e;
	e = ep;
	ep->key = (char*)malloc(strlen(key)+1);
	strcpy (ep->key, key);
	ep->value = (char*)malloc(strlen(value)+1);
	strcpy (ep->value, value);

}

void SetKeyValue (entity_t *ent, const char *key, const char *value)
{
	if (ent == NULL)
  {
    Sys_FPrintf(SYS_ERR, "ERROR: SetKeyValue: NULL entity \n");
		return;
  }

	if (!key || !key[0])
  {
    Sys_FPrintf(SYS_ERR, "ERROR: SetKeyValue: NULL or zero-length key\n");
		return;
  }

  SetKeyValue(ent->epairs, key, value);
  /*!
  \todo TODO broadcast this through a clean messaging API ;-)
  */
  Entity_OnKeyValueChanged(ent, key, value);
}

void 	DeleteKey (epair_t *&e, const char *key)
{
	epair_t	**ep, *next;
	
	ep = &e;
	while (*ep)
	{
		next = *ep;
		if ( !strcmp (next->key, key) )
		{
			*ep = next->next;
			free(next->key);
			free(next->value);
			free(next);
			return;
		}
		ep = &next->next;
	}
}

void 	DeleteKey (entity_t *ent, const char *key)
{
  DeleteKey(ent->epairs, key);
  Entity_OnKeyValueChanged(ent, key, "");
}

float	FloatForKey (entity_t *ent, const char *key)
{
	const char	*k;
	
	k = ValueForKey (ent, key);
	return (float) atof(k);
}

int IntForKey (entity_t *ent, const char *key)
{
	const char	*k;
	
	k = ValueForKey (ent, key);
	return atoi(k);
}

void 	GetVectorForKey (entity_t *ent, const char *key, vec3_t vec)
{
	const char	*k;
	
	k = ValueForKey (ent, key);
	sscanf (k, "%f %f %f", &vec[0], &vec[1], &vec[2]);
}

/*
===============
Entity_FreeEpairs

Frees the entity epairs.
===============
*/
void Entity_FreeEpairs(entity_t *e)
{
	epair_t	*ep, *next;

	for (ep = e->epairs; ep; ep = next)
	{
		next = ep->next;
		free (ep->key);
		free (ep->value);
		free (ep);
	}
	e->epairs = NULL;
}

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

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;
}

void Entity_LinkBrush (entity_t *e, brush_t *b)
{
	if (b->oprev || b->onext)
		Error ("Entity_LinkBrush: Already linked");
	b->owner = e;

//	b->onext = e->brushes.onext;
//	b->oprev = &e->brushes;
//	e->brushes.onext->oprev = b;
//	e->brushes.onext = b;
  /*
  SPoG - changed to add brushes to end of list instead of start - so this can be used by map loader.
  This could concievably cause a problem if someone is traversing e->brushes while calling this function.
  So don't.
  */
  b->onext = &e->brushes;
	b->oprev = e->brushes.oprev;
	e->brushes.oprev->onext = b;
	e->brushes.oprev = b;
}

void Entity_UnlinkBrush (brush_t *b)
{
	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;
}

// for undo
int Entity_MemorySize(entity_t *e)
{
	epair_t	*ep;
	int size = 0;

	for (ep = e->epairs; ep; ep = ep->next)
	{
    size += strlen(ep->key);
    size += strlen(ep->value);
    size += sizeof(epair_t);
	}
  size += sizeof(entity_t);
	return size;
}

epair_t* Entity_AllocateEpair(const char *key, const char *value)
{
  epair_t *ep = (epair_t*)malloc (sizeof(*ep));
  ep->key = (char*)malloc(strlen(key)+1);
  strcpy (ep->key, key);
  ep->value = (char*)malloc(strlen(value)+1);
  strcpy (ep->value, value);
  ep->next = NULL;
  return ep;
}

epair_t** Entity_GetKeyValList(entity_t *e)
{
  return &e->epairs;
}

void Entity_SetKeyValList(entity_t *e, epair_t* ep)
{
  if( e->epairs )
    Sys_Printf( "Warning : pe->epairs != NULL in Entity_SetKeyValList, will not set\n" );
  else {
    e->epairs = ep;

    for (epair_t *pe_ep = e->epairs; pe_ep; pe_ep = pe_ep->next)
      Entity_OnKeyValueChanged(e, pe_ep->key, pe_ep->value);
  }
}


/*!
\todo FIXME TTimo
this is meant to raise messages instead of calling the IEdit directly
*/
static void Entity_OnKeyValueChanged(entity_t *e, const char *key, const char* value)
{
  if(strcmp(key,"classname") == 0)
  {
    e->eclass = Eclass_ForName(value, false);
    Entity_UpdateClass(e, value);
    if(strcmp(value,"light") == 0)
      for(epair_t* ep = e->epairs; ep != NULL; ep=ep->next)
        Light_OnKeyValueChanged(e, ep->key, ep->value);
    if(e->model.pEdit)
      for(epair_t* ep = e->epairs; ep != NULL; ep=ep->next)
        e->model.pEdit->OnKeyValueChanged(e, ep->key, ep->value);
  }
  else if(Entity_IsLight(e))
    Light_OnKeyValueChanged(e, key, value);
  else if(e->model.pEdit)
    e->model.pEdit->OnKeyValueChanged(e, key, value);

  // update brush mins/maxs for legacy culling system
  if(e->model.pRender && e->brushes.onext != &e->brushes)
    Brush_Build( e->brushes.onext, true, true, false, true );
}