#ifndef list_h
#define list_h

// BaseList.h --
//	Interface for class BaseList, current implementation is as an
//	array of ent's.  This implementation was chosen to optimize
//	getting to the ent's rather than inserting and deleting.
//	Also pairs of append's and get's act like push's and pop's
//	and are very efficient.  The only really expensive operations
//	are inserting (but not appending), which requires pushing every
//	element up, and resizing the list, which involves getting new space
//	and moving the data.  Resizing occurs automatically when inserting
//	more elements than the list can currently hold.  Automatic
//	resizing is done one "chunk_size" of elements at a time and
//	always increases the size of the list.  Resizing to zero
//	(or to less than the current value of num_entries)
//	will decrease the size of the list to the current number of
//	elements.  Resize returns the new max_entries.
//
//	Entries must be either a pointer to the data or nonzero data with
//	sizeof(data) <= sizeof(void*).

#include <stdarg.h>
#include "util.h"

typedef void* ent;
typedef int (*list_cmp_func)(const void* v1, const void* v2);

class BaseList {
public:
	~BaseList()		{ clear(); }

	void clear();		// remove all entries
	int length() const	{ return num_entries; }
	int chunk() const	{ return chunk_size; }
	int max() const		{ return max_entries; }
	int resize(int = 0);	// 0 => size to fit current number of entries

	void sort(list_cmp_func cmp_func);

	int MemoryAllocation() const
		{ return padded_sizeof(*this) + pad_size(max_entries * sizeof(ent)); }

protected:
	explicit BaseList(int = 0);
	BaseList(BaseList&);

	void insert(ent);	// add at head of list

	// Assumes that the list is sorted and inserts at correct position.
	void sortedinsert(ent, list_cmp_func cmp_func);

	void append(ent);	// add to end of list
	ent remove(ent);	// delete entry from list
	ent remove_nth(int);	// delete nth entry from list
	ent get();		// return and remove ent at end of list
	ent last()		// return at end of list
		{ return entry[num_entries-1]; }

	// Return 0 if ent is not in the list, ent otherwise.
	ent is_member(ent) const;

	// Returns -1 if ent is not in the list, otherwise its position.
	int member_pos(ent) const;

	ent replace(int, ent);	// replace entry #i with a new value

	// Return nth ent of list (do not remove).
	ent operator[](int i) const
		{
#ifdef SAFE_LISTS
		if ( i < 0 || i > num_entries-1 )
			return 0;
		else
#endif
			return entry[i];
		}

	void operator=(BaseList&);

	ent* entry;
	int chunk_size;		// increase size by this amount when necessary
	int max_entries;
	int num_entries;
	};


// List.h -- interface for class List
//	Use:	to get a list of pointers to class foo you should:
//		1) typedef foo* Pfoo; (the macros don't like explicit pointers)
//		2) declare(List,Pfoo); (declare an interest in lists of Pfoo's)
//		3) variables are declared like:
//				List(Pfoo) bar;	(bar is of type list of Pfoo's)

// For lists of "type".

#define List(type) type ## List

// For lists of pointers to "type"
#define PList(type) type ## PList

#define Listdeclare(type)						\
struct List(type) : BaseList						\
	{								\
	explicit List(type)(type ...);						\
	List(type)() : BaseList(0) {}					\
	explicit List(type)(int sz) : BaseList(sz) {}				\
	List(type)(List(type)& l) : BaseList((BaseList&)l) {}		\
									\
	void operator=(List(type)& l)					\
		{ BaseList::operator=((BaseList&)l); }			\
	void insert(type a)	{ BaseList::insert(ent(a)); }		\
	void sortedinsert(type a, list_cmp_func cmp_func)		\
		{ BaseList::sortedinsert(ent(a), cmp_func); }		\
	void append(type a)	{ BaseList::append(ent(a)); }		\
	type remove(type a)						\
			{ return type(BaseList::remove(ent(a))); }	\
	type remove_nth(int n)	{ return type(BaseList::remove_nth(n)); }\
	type get()		{ return type(BaseList::get()); }	\
	type last()		{ return type(BaseList::last()); }	\
	type replace(int i, type new_type)				\
		{ return type(BaseList::replace(i,ent(new_type))); }	\
	type is_member(type e) const					\
		{ return type(BaseList::is_member(ent(e))); }		\
	int member_pos(type e) const					\
		{ return BaseList::member_pos(ent(e)); }		\
									\
	type operator[](int i) const					\
		{ return type(BaseList::operator[](i)); }		\
	};								\

#define Listimplement(type)						\
List(type)::List(type)(type e1 ...) : BaseList()			\
	{								\
	append(e1);							\
	va_list ap;							\
	va_start(ap,e1);						\
	for ( type e = va_arg(ap,type); e != 0; e = va_arg(ap,type) )	\
		append(e);						\
	resize();							\
	}

#define PListdeclare(type)						\
struct PList(type) : BaseList						\
	{								\
	explicit PList(type)(type* ...);						\
	PList(type)() : BaseList(0) {}					\
	explicit PList(type)(int sz) : BaseList(sz) {}				\
	PList(type)(PList(type)& l) : BaseList((BaseList&)l) {}		\
									\
	void operator=(PList(type)& l)					\
		{ BaseList::operator=((BaseList&)l); }			\
	void insert(type* a)	{ BaseList::insert(ent(a)); }		\
	void sortedinsert(type* a, list_cmp_func cmp_func)		\
		{ BaseList::sortedinsert(ent(a), cmp_func); }		\
	void append(type* a)	{ BaseList::append(ent(a)); }		\
	type* remove(type* a)						\
		{ return (type*)BaseList::remove(ent(a)); }		\
	type* remove_nth(int n)	{ return (type*)(BaseList::remove_nth(n)); }\
	type* get()		{ return (type*)BaseList::get(); }	\
	type* operator[](int i) const					\
		{ return (type*)(BaseList::operator[](i)); }		\
	type* replace(int i, type* new_type)				\
		{ return (type*)BaseList::replace(i,ent(new_type)); }	\
	type* is_member(type* e)					\
		{ return (type*)BaseList::is_member(ent(e)); }		\
	int member_pos(type* e)						\
		{ return BaseList::member_pos(ent(e)); }		\
	};								\

#define PListimplement(type)						\
PList(type)::PList(type)(type* ep1 ...) : BaseList()			\
	{								\
	append(ep1);							\
	va_list ap;							\
	va_start(ap,ep1);						\
	for ( type* ep = va_arg(ap,type*); ep != 0;			\
	      ep = va_arg(ap,type*) )					\
		append(ep);						\
	resize();							\
	}


#define declare(metatype,type) metatype ## declare (type)

// Popular type of list: list of strings.
declare(PList,char);
typedef PList(char) name_list;

// Macro to visit each list element in turn.
#define loop_over_list(list, iterator)  \
	int iterator;	\
	for ( iterator = 0; iterator < (list).length(); ++iterator )

#endif /* list_h */
