//////////////////////////////////////////////////////////
//	actor list 
//
//	builds a list of a specific entity type which can be 
//	used for searches without going through the entire entity
//	list (which could be as large as 600 entities)
//
//	alists are destroyed each time a level ends and rebuilt
//	in worldspawn_prethink
//
//	globals:
//	actor_list - all entities with FL_CLIENT, FL_BOT, or FL_MONSTER
//		flags set.
//	client_bot_list - all entities with FL_CLIENT or FL_BOT flags
//		set.  Used by ai routines for targetting
//	client_list - all entities with FL_CLIENT set, ie. human players
//		who have logged on to the server.  Used for events that only
//		concern human players.
//	monster_list - all entities with FL_MONSTER flag set.
//////////////////////////////////////////////////////////

#include	"world.h"
#include	"actorlist.h"

///////////////////////////////////////////////////////////////////////////////////////////////
// external globals
///////////////////////////////////////////////////////////////////////////////////////////////

alist_t	*actor_list;		//	all entities with FL_CLIENT, FL_BOT or FL_MONSTER flags set
alist_t	*client_list;		//	all entities with FL_CLIENT flag set
alist_t	*monster_list;		//	all entities with FL_MONSTER flag set
alist_t	*bot_list;			//	all entities with FL_BOT flag set
alist_t	*client_bot_list;	//	all entities with FL_BOT or FL_CLIENT flags set
alist_t	*monster_bot_list;	//	all entities with FL_MONSTER or FL_BOT flags set
//alist_t	*item_list;			//	all entities with FL_ITEM set

///////////////////////////////////////////////////////////////////////////////////////////////
// returns an initialized (0 member) alist
///////////////////////////////////////////////////////////////////////////////////////////////

alist_t	*alist_init (void)
{
	alist_t		*alist;

	alist = (alist_t *) com->malloc (sizeof (alist_t), MEM_MALLOC);

	alist->head = NULL;
	alist->current = NULL;
	alist->tail = NULL;
	alist->count = 0;

	return	alist;
}

///////////////////////////////////////////////////////////////////////////////////////////////
//	alist_add
//	adds an entity to the end of alist, unless that entity is already in the list
///////////////////////////////////////////////////////////////////////////////////////////////

void	alist_add (alist_t *alist, userEntity_t *ent)
{
	alistNode_t	*node;

	if (!alist)
		com->Error ("Attempted to add to an empty alist!\n");

	// make sure this entity is not already in the list
	node = alist->head;
	while (node)
	{
		if (node->ent == ent)
			return;
		node = node->next;
	}

	// allocate the new node
	node = (alistNode_t *) com->malloc (sizeof (alistNode_t), MEM_MALLOC);
	node->ent = ent;
	node->next = NULL;

	alist->count++;

	// nothing in the list
	if (alist->head == NULL)
	{
		alist->head = node;
		alist->current = node;
		alist->tail = node;
	}
	// one or more items in the list
	else
	{
		alist->tail->next = node;
		alist->tail = node;
	}
}

///////////////////////////////////////////////////////////////////////////////////////////////
//	alist_remove
//	adds an entity to the end of alist
///////////////////////////////////////////////////////////////////////////////////////////////

void	alist_remove (alist_t	*alist, userEntity_t *ent)
{
	alistNode_t	*node, *prev;

	if (!alist->head)
		com->Error ("Attempted to remove from an empty alist!\n");

	node = alist->head;

	// are we deleting the first node in the list?
	if (node->ent == ent)
	{
		prev = NULL;
		// move the head to the next entity
		alist->head = node->next;
	}
	else
	{
		// traverse to the node pointing to ent
		while (node && node->ent != ent)
		{
			prev = node;
			node = node->next;
		}

		// ent is not in this list
		if (!node)
			return;

		//	point the previous node to skip over the one we're deleting
		prev->next = node->next;
	}		
	
	//	if this is the tail we're deleting, point the tail to 
	//	the previous node
	if (node == alist->tail)
		alist->tail = prev;
	//	if this is the current node we're deleting, point the 
	//	current node to the previous node
	if (node == alist->current)
		alist->current = prev;

	alist->count--;

	// deallocate the memory
	com->free (node);
}

///////////////////////////////////////////////////////////////////////////////////////////////
//	alist_FirstEntity
// 
// returns the head of the list and initializes alist->current to the head of the list
///////////////////////////////////////////////////////////////////////////////////////////////

userEntity_t	*alist_FirstEntity (alist_t *alist)
{
	// make current point to the head
	alist->current = alist->head;

	if (alist->current)
		return	alist->current->ent;
	else
		return	NULL;
}

///////////////////////////////////////////////////////////////////////////////////////////////
//	alist_NextEntity
//	returns the ent in the next node of the list from alist->current, or null if it is the end of
//	the alist
//	
//	ALWAYS call alist_FirstEntity once before calling alist_NextEntity, or the current pointer
//	could point to anywhere in the list
///////////////////////////////////////////////////////////////////////////////////////////////

userEntity_t	*alist_NextEntity (alist_t *alist)
{
	if (alist->current)
	{
		alist->current = alist->current->next;

		if (alist->current)
			return	alist->current->ent;
	}

	return	NULL;
}

///////////////////////////////////////////////////////////////////////////////////////////////
//	alist_build
//
//	builds a list of all entities whose flags have at least one of the same bits set as
//	include_flag
///////////////////////////////////////////////////////////////////////////////////////////////

void	alist_build (alist_t *alist, int include_flag)
{
	userEntity_t	*head;
	int				count = 0;

	head = gstate->FirstEntity ();

	while (head)
	{
		// if head has matching flags, add head to the list of entities
		if (head->flags & include_flag)
		{
			alist_add (alist, head);
//			gstate->Con_Dprintf ("added %s to list\n", head->className);
			count++;
		}

		head = gstate->NextEntity (head);
	}

//	gstate->Con_Printf ("count = %i\n", count);
}
