//-----------------------------------------------------------------------------
//
//  $Logfile:: /GameSource/container.h                                        $
// $Revision:: 3                                                              $
//   $Author:: Burger                                                         $
//     $Date:: 12/27/00 8:34p                                                 $
//
// Copyright (C) 1997 by Ritual Entertainment, Inc.
// All rights reserved.
//
// This source is may not be distributed and/or modified without
// expressly written permission by Ritual Entertainment, Inc.
//
// DESCRIPTION:
// Base class for a dynamic array.  Allows adding, removing, index of,
// and finding of entries with specified value.  Originally created for
// cataloging entities, but pointers to objects that may be removed at
// any time are bad to keep around, so only entity numbers should be
// used in the future.
//

#ifndef __CONTAINER_H__
#define __CONTAINER_H__

#include "g_local.h"
#include <stdlib.h>

extern game_import_t gi;

template< class Type >
class /**/ Container
{
private:
	Type*   objlist;
	int numobjects;
	int maxobjects;

public:
	/*Container::*/
	Container();
	/*Container::*/ ~Container/*<Type>*/ ();
	void        FreeObjectList( void );
	void        ClearObjectList( void );
	int     NumObjects( void ) const;
	void        Resize( int maxelements );
	void        SetObjectAt( int index, Type& obj );
	int     AddObject( const Type& obj );
	int     AddUniqueObject( Type& obj );
	void    AddObjectAt( int index, Type& obj );
	int     IndexOfObject( const Type& obj );
	qboolean    ObjectInList( const Type& obj );
	Type&       ObjectAt( int index );
	Type*       AddressOfObjectAt( int index );
	void        RemoveObjectAt( int index );
	void        RemoveObject( const Type& obj );
	void        Sort( int ( /*__cdecl*/ *compare )( const void* elem1, const void* elem2 ) );
//      virtual void Archive(	Archiver &arc );
//      virtual void Unarchive( Archiver &arc );
};

template< class Type >Container<Type>::Container()
{
	objlist = NULL;
	FreeObjectList();
}

template< class Type >Container<Type>::~Container()
{
	FreeObjectList();
}

template< class Type >
void Container<Type>::FreeObjectList( void )
{
	if( objlist )
	{
		delete[] objlist;
	}
	objlist = NULL;
	numobjects = 0;
	maxobjects = 0;
}

template< class Type >
void Container<Type>::ClearObjectList( void )
{
	// only delete the list if we have objects in it
	if( objlist && numobjects )
	{
		delete[] objlist;
		objlist = new Type[ maxobjects ];
		numobjects = 0;
	}
}

template< class Type >
int Container<Type>::NumObjects( void ) const
{
	return numobjects;
}

template< class Type >
void Container<Type>::Resize( int maxelements )
{
	Type* temp;
	int i;

	assert( maxelements >= 0 );

	if( maxelements <= 0 )
	{
		FreeObjectList();
		return;
	}

	if( !objlist )
	{
		maxobjects = maxelements;
		objlist = new Type[ maxobjects ];
	}
	else
	{
		temp = objlist;
		maxobjects = maxelements;
		if( maxobjects < numobjects )
		{
			maxobjects = numobjects;
		}

		objlist = new Type[ maxobjects ];
		for( i = 0; i < numobjects; i++ )
		{
			objlist[ i ] = temp[ i ];
		}
		delete[] temp;
	}
}

template< class Type >
void Container<Type>::SetObjectAt( int index, Type& obj )
{
	if( ( index <= 0 ) || ( index > numobjects ) )
	{
		gi.error( "Container::SetObjectAt : index out of range" );
	}

	objlist[ index - 1 ] = obj;
}

template< class Type >
int Container<Type>::AddObject( const Type& obj )
{
	if( !objlist )
	{
		Resize( 10 );
	}

	if( numobjects == maxobjects )
	{
		Resize( maxobjects * 2 );
	}

	objlist[ numobjects ] = ( Type& )obj;
	numobjects++;

	return numobjects;
}

template< class Type >
int Container<Type>::AddUniqueObject( Type& obj )
{
	int index;

	index = IndexOfObject( obj );
	if( !index )
		index = AddObject( obj );
	return index;
}

template< class Type >
void Container<Type>::AddObjectAt( int index, Type& obj )
{
	//
	// this should only be used when reconstructing a list that has to be identical to the original
	//
	if( index > maxobjects )
	{
		Resize( index );
	}
	if( index > numobjects )
	{
		numobjects = index;
	}
	SetObjectAt( index, obj );
}

template< class Type >
int Container<Type>::IndexOfObject( const Type& obj )
{
	int i;

	for( i = 0; i < numobjects; i++ )
	{
		if( objlist[ i ] == obj )
		{
			return i + 1;
		}
	}

	return 0;
}

template< class Type >
qboolean Container<Type>::ObjectInList( const Type& obj )
{
	if( !IndexOfObject( obj ) )
	{
		return false;
	}

	return true;
}

template< class Type >
Type& Container<Type>::ObjectAt( int index )
{
	if( ( index <= 0 ) || ( index > numobjects ) )
	{
		gi.error( "Container::ObjectAt : index %d out of range of %d", index, numobjects );
	}

	return objlist[ index - 1 ];
}

template< class Type >
Type* Container<Type>::AddressOfObjectAt( int index )
{
	//
	// this should only be used when reconstructing a list that has to be identical to the original
	//
	if( index > maxobjects )
	{
		gi.error( "Container::AddressOfObjectAt : index is greater than maxobjects" );
	}
	if( index > numobjects )
	{
		numobjects = index;
	}
	return ( Type* )&objlist[ index - 1 ];
}

template< class Type >
void Container<Type>::RemoveObjectAt( int index )
{
	int i;

	if( !objlist )
	{
		gi.dprintf( "Container::RemoveObjectAt : Empty list\n" );
		return;
	}

	if( ( index <= 0 ) || ( index > numobjects ) )
	{
		gi.error( "Container::RemoveObjectAt : index out of range" );
		return;
	}

	i = index - 1;
	numobjects--;
	for( i = index - 1; i < numobjects; i++ )
	{
		objlist[ i ] = objlist[ i + 1 ];
	}
}

template< class Type >
void Container<Type>::RemoveObject( const Type& obj )
{
	int index;

	index = IndexOfObject( obj );
	if( !index )
	{
		gi.dprintf( "Container::RemoveObject : Object not in list\n" );
		return;
	}
	RemoveObjectAt( index );
}

template< class Type >
void Container<Type>::Sort( int ( /*__cdecl*/ *compare )( const void* elem1, const void* elem2 ) )
{
	if( !objlist )
	{
		gi.dprintf( "Container::RemoveObjectAt : Empty list\n" );
		return;
	}

	qsort( ( void* )objlist, ( size_t )numobjects, sizeof( Type ), compare );
}

//
// Exported templated classes must be explicitly instantiated
//
#endif /* container.h */
