/*
**	Command & Conquer Renegade(tm)
**	Copyright 2025 Electronic Arts Inc.
**
**	This program 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.
**
**	This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
*/

/***********************************************************************************************
 ***              C O N F I D E N T I A L  ---  W E S T W O O D  S T U D I O S               ***
 ***********************************************************************************************
 *                                                                                             *
 *                 Project Name : WWLib                                                        *
 *                                                                                             *
 *                     $Archive:: /Commando/Code/wwlib/multilist.h                            $*
 *                                                                                             *
 *              Original Author:: Greg Hjelstrom                                               *
 *                                                                                             *
 *                      $Author:: Jani_p                                                      $*
 *                                                                                             *
 *                     $Modtime:: 4/09/01 8:26p                                               $*
 *                                                                                             *
 *                    $Revision:: 16                                                          $*
 *                                                                                             *
 *---------------------------------------------------------------------------------------------*
 * Functions:                                                                                  *
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */


#if defined(_MSC_VER)
#pragma once
#endif

#ifndef MULTILIST_H
#define MULTILIST_H

#include "always.h"
#include "mempool.h"
#include <assert.h>

class MultiListNodeClass;
class GenericMultiListClass;


/******************************************************************************
	
	MultiLists

	MultiLists solve the problem of needing to have objects that can be placed
	into multiple lists at the same time.  MultiListNodes are all allocated from
	a pool which grows in chunks to avoid overhead from calling new and delete.
	All operations such as insertion, removal, checking if a list contains an
	object, are ether immediate or O(numlists) where numlists is the number of
	different lists that the object in question currently occupies.

******************************************************************************/


/**
** MultiListObjectClass
** This is an object that can be linked into a MultiList.  The only overhead
** for this is a single pointer to a MultiListNode.
** Objects that are linked into MulitLists must derive from this class.  
** If you delete an instance of one of these objects while it is in one or more
** Multi-Lists, it will automatically remove itself from the lists.
*/
class MultiListObjectClass 
{
public:

	MultiListObjectClass(void) : ListNode(NULL)								{ }
	virtual ~MultiListObjectClass(void);

	MultiListNodeClass *		Get_List_Node() const							{ return ListNode; }
	void							Set_List_Node(MultiListNodeClass *node)	{ ListNode = node; }

private:
	MultiListNodeClass *		ListNode;
};


/**
** MultiListNodeClass
** These nodes allow objects to be linked in multiple lists.  It is
** like a 2-D linked list where one dimension is the list of objects in a 
** given list and the other dimension is the list of lists that a given object
** is in.
*/
class MultiListNodeClass : public AutoPoolClass<MultiListNodeClass, 256>
{
public:
	MultiListNodeClass(void) { Prev = Next = NextList = 0; Object = 0; List = 0; }

	MultiListNodeClass		*Prev;					// prev object in list
	MultiListNodeClass		*Next;					// next object in list
	MultiListNodeClass		*NextList;				// next list this object is in
	MultiListObjectClass		*Object;					// pointer back to the object
	GenericMultiListClass	*List;					// pointer to list for this node
};


/**
** GenericMultiListClass
** This is an internal implementation class for Multi List.  It is not meant to be used as-is
** Instead use the MultiListClass template which performs type-casting and reference counting
** on the objects that you add to the list.
** This simply contains the head node for a list.  This is a doubly circularly linked list where
** our head node is a sentry.  To easily iterate the list use the iterator defined below.
*/
class GenericMultiListClass 
{
public:

	GenericMultiListClass(void)	{ Head.Next = Head.Prev = &Head; Head.Object = 0; Head.NextList = 0; }
	virtual ~GenericMultiListClass(void);
	
	bool							Is_In_List(MultiListObjectClass *obj);
	bool							Contains(MultiListObjectClass * obj);
	bool							Is_Empty(void);
	int							Count(void);

protected:
	
	bool							Internal_Add(MultiListObjectClass *obj,bool onlyonce = true);
	bool							Internal_Add_Tail(MultiListObjectClass * obj,bool onlyonce = true);
	bool							Internal_Add_After(MultiListObjectClass * obj,const MultiListObjectClass * existing_list_member,bool onlyonce = true);
	bool							Internal_Remove(MultiListObjectClass *obj);

	MultiListObjectClass	*	Internal_Get_List_Head(void);
	MultiListObjectClass	*	Internal_Remove_List_Head(void);

private:

	MultiListNodeClass		Head;
	friend class				GenericMultiListIterator;
	friend class				MultiListObjectClass;
};

inline bool GenericMultiListClass::Is_In_List(MultiListObjectClass * obj)
{
	return Contains(obj);
}

inline bool GenericMultiListClass::Is_Empty(void)
{
	return (Head.Next == &Head);
}

inline MultiListObjectClass * GenericMultiListClass::Internal_Get_List_Head(void)
{
	if (Head.Next == &Head) {
		return 0;					// no more objects
	} else {
		assert(Head.Next->Object != 0);
		return Head.Next->Object;
	}
}



/**
** GenericMultiListIterator
** This is the internal implementation of an iterator for a MultiList.  The user should
** use the templated MultiListIterator which will do typecasting and proper reference 
** counting rather than this class.
*/
class GenericMultiListIterator
{
public:
	GenericMultiListIterator(GenericMultiListClass *list)	{ assert(list); First(list); }

	void				First(GenericMultiListClass *list)		{ List = list; CurNode = List->Head.Next; }
	void				First(void)										{ CurNode = List->Head.Next; }
	void				Next(void)										{ CurNode = CurNode->Next; }
	void				Prev(void)										{ CurNode = CurNode->Prev; }
	bool				Is_Done(void)									{ return (CurNode == &(List->Head)); }
	
protected:
	
	MultiListObjectClass	*		Current_Object(void)			{ return CurNode->Object; }

	GenericMultiListClass *		List;				// list we're working in
	MultiListNodeClass *			CurNode;			// node we're currently at.

};



/**************************************************************************************

  MultiListClass, MultiListIterator

  These templates inherits from GenericMultiListClass and GenericMultiListIterator
  and handle all typecasting to your object type.  These classes require that
  your 'ObjectType' be derived from MultiListObjectClass.

**************************************************************************************/

/**
** MultiListClass
** This is a template derived from GenericMultiListClass which allows you
** to create specialized lists.  All this template does is perform type-checking
** and internal type-casting of all pointers to your object type.  Your
** object must be derived from MultiListObjectClass in order to be used
** with this template.
*/
template <class ObjectType>
class MultiListClass : public GenericMultiListClass
{
public:

	MultiListClass(void) { }
			
	virtual ~MultiListClass(void)
	{
		while (!Is_Empty()) {
			Remove_Head();
		}
	}

	bool				Add(ObjectType * obj,bool onlyonce = true)
	{
		return Internal_Add(obj,onlyonce);
	}	

	bool				Add_Tail(ObjectType * obj,bool onlyonce = true)
	{
		return Internal_Add_Tail(obj,onlyonce);
	}

	bool				Add_After(ObjectType * obj,const ObjectType * existing_list_member,bool onlyonce = true)
	{
		return Internal_Add_After(obj,existing_list_member,onlyonce);
	}

	bool				Remove(ObjectType *obj)
	{
		return Internal_Remove(obj);
	}

	ObjectType *	Get_Head()
	{
		return ((ObjectType*)Internal_Get_List_Head());
	}

	ObjectType *	Peek_Head()
	{
		return ((ObjectType*)Internal_Get_List_Head());
	}

	ObjectType *	Remove_Head()
	{
		return ((ObjectType*)Internal_Remove_List_Head());
	}

	void				Reset_List()
	{
		while (Get_Head() != NULL) {
			Remove_Head();
		}
	}

private:

	// not implemented
	MultiListClass(const MultiListClass & that);
	MultiListClass & operator = (const MultiListClass & that);
	
};

/**
** MultiListIterator
** This is a template derived from GenericMultiListIterator which allows you
** to iterate through specialized MultiListClass's.  Again, it basically
** just type-casts all of the pointers for you (hopefully compiles away to
** nothing...)
**
** WARNING: If you need to remove an object from a MultiList while you are iterating, use the
** Remove_Current_Object function (don't modify the list directly while iterating it).
*/
template <class ObjectType>
class MultiListIterator : public GenericMultiListIterator
{
public:

	MultiListIterator(MultiListClass<ObjectType> *list) : GenericMultiListIterator(list)	{}

	ObjectType *	Get_Obj(void)
	{
		return (ObjectType*)Current_Object();
	}

	ObjectType *	Peek_Obj(void)
	{
		return (ObjectType*)Current_Object();
	}

	void				Remove_Current_Object(void)
	{
		ObjectType * obj = Peek_Obj();
		if (obj != NULL) {
			Next();
			((MultiListClass<ObjectType> *)List)->Remove(obj);
		}
	}

};


/**************************************************************************************

  RefMultiListClass, RefMultiListIterator

  These templates inherits from GenericMultiListClass and GenericMultiListIterator
  and handle all typecasting to your object type and also do proper reference tracking.
  These classes require that your 'ObjectType' be derived from MultiListObjectClass and
  RefCountClass.

**************************************************************************************/

/**
** RefMultiListClass
** This is a template derived from GenericMultiListClass which handles ref-counted
** objects.  It assumes that 'ObjectType' is derived from MultiListObjectClass and
** RefCountClass.   It adds type-checking and reference counting to 
** GenericMultiListClass.
*/
template <class ObjectType>
class RefMultiListClass : public GenericMultiListClass
{
public:

	virtual			~RefMultiListClass(void)
	{
		while (!Is_Empty()) {
			Release_Head();
		}
	}

	bool				Add(ObjectType * obj,bool onlyonce = true)
	{
		// if we add the object from our list, add a reference to it
		bool result = Internal_Add(obj,onlyonce);
		if (result == true) {
			obj->Add_Ref();
		}
		return result;
	}	

	bool				Add_Tail(ObjectType * obj,bool onlyonce = true)
	{
		// if we add the object from our list, add a reference to it
		bool result = Internal_Add_Tail(obj,onlyonce);
		if (result == true) {
			obj->Add_Ref();
		}
		return result;
	}

	bool				Add_After(ObjectType * obj,const ObjectType * existing_list_member,bool onlyonce = true)
	{
		// if we add the object from our list, add a reference to it
		bool result = Internal_Add_After(obj,existing_list_member,onlyonce);
		if (result == true) {
			obj->Add_Ref();
		}
		return result;
	}

	bool				Remove(ObjectType *obj)
	{
		// if we remove the object from our list, release our reference to it
		bool result = Internal_Remove(obj);
		if (result) {
			obj->Release_Ref();
		}
		return result;
	}

	bool				Release_Head(void)
	{
		// remove the head from the list and release our reference to it
		ObjectType * obj = ((ObjectType*)Internal_Remove_List_Head());
		if (obj) {
			obj->Release_Ref();
			return true;
		} else {
			return false;
		}
	}

	ObjectType *	Get_Head()
	{
		// if we have a head, add a reference for the caller of this function
		ObjectType * obj = ((ObjectType*)Internal_Get_List_Head());
		if (obj) {
			obj->Add_Ref();
		}
		return obj;
	}

	ObjectType *	Peek_Head()
	{
		// no need to add-ref since the caller is 'peek'ing
		return ((ObjectType*)Internal_Get_List_Head());
	}

	ObjectType *	Remove_Head()
	{
		// our reference is transferred to the caller of this function
		return ((ObjectType*)Internal_Remove_List_Head());
	}

	void				Reset_List()
	{
		while (Peek_Head() != NULL) {
			Release_Head();
		}
	}
};

/**
** RefMultiListIterator
** This is a template derived from GenericMultiListIterator which can iterate
** through RefMultiListClass's.  It adds type-checking and reference counting
** to GenericMultiListIterator.
**
** WARNING: If you need to remove an object from a MultiList while you are iterating, use the
** Remove_Current_Object function (don't modify the list directly while iterating it).  Also
** note that this function will cause the list to release its reference to the object.
*/
template <class ObjectType>
class RefMultiListIterator : public GenericMultiListIterator
{
public:

	RefMultiListIterator(RefMultiListClass<ObjectType> *list) : GenericMultiListIterator(list) {}

	ObjectType *	Get_Obj(void)
	{
		ObjectType * obj = (ObjectType*)Current_Object();
		if (obj != NULL) {
			obj->Add_Ref();
		}
		return obj;
	}

	ObjectType *	Peek_Obj(void)
	{
		return ((ObjectType*)Current_Object());
	}

	void				Remove_Current_Object(void)
	{
		ObjectType * obj = Peek_Obj();
		if (obj != NULL) {
			Next();
			((RefMultiListClass<ObjectType> *)List)->Remove(obj);
		}
	}
};



/**************************************************************************************

  PriorityMultiListIterator

  This template inherits from MultiListIterator and is used to implement a simple
  priority queue where nodes are popped off the head of the list and added to the tail.

**************************************************************************************/

template <class ObjectType>
class PriorityMultiListIterator : public MultiListIterator<ObjectType>
{
public:
	PriorityMultiListIterator(MultiListClass<ObjectType> *list)
		:	OriginalHead (NULL),
			MultiListIterator<ObjectType>(list)			{ First (); }

	bool
	Process_Head (ObjectType **object)
	{
		bool retval = false;

		//	Check to ensure we don't wrap around the list (stop after iterating
		// the list once).
		if (CurNode != NULL && CurNode->Object != NULL && OriginalHead != CurNode) {
			OriginalHead		= (OriginalHead == NULL) ? CurNode : OriginalHead;
			(*object)			= (ObjectType *)CurNode->Object;


			// Remove the node from the head of the list and
			// add it to the tail of the list
			Remove_Current_Object();
			((MultiListClass<ObjectType> *)PriorityMultiListIterator::List)->Add_Tail ((*object));

			retval = true;
		}

		return retval;
	}
	
protected:
	
	MultiListNodeClass *		OriginalHead;
};


#endif //LIST_CLASS_H