#ifndef __XCOLLECTION_INCLUDED
#define __XCOLLECTION_INCLUDED

/*
**  Overview:
**  
** With the first part, we define some 'generic data'  like xCollection and xCollectionData
** With the second part we define some macros simulating C++ templates,
** so that we can declare and use type-specific Collections like CollectionOf_TYPEA, CollectionOf_TYPEB, ...
*/

/*
** xCollection is a generic container of xCollectionData.
** xCollectionData is a one-word value which is typically a pointer to a data structure holding specific data.
**
** WARNING:
** ============================================================================
** xCollection functions cannot be type-checked,
** and therefore require careful use in order to avoid serious errors.
** ============================================================================
** All the items in a xCollection should be of the same type.
** You must decide who is the owner of the data once they are added to a xCollection.
** Apart from trivial cases where xCollectionData is a one-word scalar (i.e  an integer..),
** in every other cases xCollectionData is a pointer-to-some-data, and in these cases
** you should establish who is responsible for the destruction/deallocation of these data.
** To be more precise:
**   * when an item is removed from an xCollection, who is responsible for the destruction/deallocation of the memory block 'pointed' by an XCollectionItem ?
**   * when an xCollection is destroyed, who is responsible to free all the existing xCollectionData ?
** If xCollectionData is just a 'reference' to those data, then the job
**   is easy: you should do nothing; the reference is simply removed.
**   BUT
** If the added Items should belongs to the xCollection, you MUST declare (when the xCollection is initialized)
** a custom item-destructor.
** This item-destructor is a callback that will be automatically invoked when an item is removed or the whole collection is destroyed.
** This callback receives just one parameter: the xCollectionData (that is, a pointer to a data-structure)
** and its responsability is to cleanup/destroy the data-structure members and then to free
** the memory.
**
*/
#include <stdarg.h>

typedef int xErrorCode ;  //  O is OK ....

typedef struct xCollection  xCollection;
typedef struct xCollectionEntry  xCollectionEntry;

typedef void* xCollectionData; //generic one-word value, which is typically a pointer to a data structure.

 // WARNING: *never* refer directly to any of the fields of any of the xCollection - related data structures;
 // use the proper procedures and macros defined here.
typedef struct xCollectionCursor {
	const xCollection* store;
	// Usually only curr is defined; next/prev are NULL.
	// But when the curr node is removed; curr becomes NULL and prev/next are set before removing the node
	xCollectionEntry* curr;
	xCollectionEntry* prev;
	xCollectionEntry* next;
} xCollectionCursor;

/*
 ** Prototypes for a callback function used by xCollection_Remove() and xCollection_Destroy().
 ** Set the callback with xCollection_New()
 */
typedef void (xCollection_DestroyProc)(xCollectionData data);

/*
** xCollectionNew() should be used for creating and initializing a new xCollection.
** xCollection_New() initialize the xCollection with a destructor callback (or NULL if no destructor is required).
** If not NULL, this callback will be used every time an xCollectionEntry is removed,
** including when the xCollection is destroyed ( and therefore the existing items will be automatically destroyed).
*/
xCollection* xCollection_New(xCollection_DestroyProc* fun);

/*
** Destroy the xCollection. If xCollection is NULL or invalid, do nothing.
*/
void xCollection_Destroy(xCollection* store);

/*
** Reset the xCollection ( remove all its items ). If xCollection is NULL or invalid, do nothing.
*/
void xCollection_Reset(xCollection* store);

/*
** return the number of Items in xCollection (return 0 if xCollection is NULL or invalid)
*/
int xCollection_Size(const xCollection* store);

/*
** Add an xCollectionData -
** return an xErrorCode ( 0 is OK )
** (.. no specific erro-code if xCollection is NULL or invalid, or not enough menory for the new item)
*/
xErrorCode  xCollection_Add(xCollection* store, const xCollectionData data);



/*
** WARNING:
** Since xCollectionData is usually a ptr to a data structure owned by the xCollection,
**  this value is valid as long as the xCollection's item is not removed.
*/
xCollectionData xCollection_Value(const xCollectionCursor* cursor);

/*
** Remove from the xCollection the item pointed by xCollectionCursor  -
** If xCollection was initialized with a not NULL destructor callback, it will be used to clear the memory pointed by xCollectionCursor.
** xCollectionCursor should be a valid record returned by xCollection_First/Last/Next/Prev
**
** The current entry becomes invalid, but you can still use xCollection_Next/Prev for moving the xCollectionCursor.
**
** return 0 (OK) or -1 (NULL or invalid xCollection, NULL or invalid xCollectionCursor)
*/
xErrorCode xCollection_RemoveAt(xCollectionCursor* cursor);


/*
** Initialize the xCollectionCursor referencing the 'first' item
** If the xCollection is NULL, invalid or empty, return an xErrorCode != 0
*/
xErrorCode xCollection_First(const xCollection* store, xCollectionCursor* cursor);
/*
** Initialize the xCollectionCursor referencing the 'last' item
** If the xCollection is NULL, invalid or empty, return an xErrorCode != 0
*/
xErrorCode xCollection_Last(const xCollection* store, xCollectionCursor* cursor);

/*
** General notes about xCollection_Next/Prev/Remove:
** -------------------------------------------------
** xCollection_Next/Prev/Remove should use an xCollectionCursor returned by
**   xCollection_First/Last  or (xCollection_Next/Prev).
** If the xCollectionCursor is NULL or invalid or empty, return an xErrorCode != 0
*/

/*
** Given a valid xCollectionCursor ( returned by xCollection_First/Last/Next/Prev ),
** move the cursor to the 'next' item.
** If there's no next-item return an xErrorCode != 0
*/
xErrorCode xCollection_Next(xCollectionCursor* cursor);
/*
** Given a valid xCollectionCursor ( returned by xCollection_First/Last/Next/Prev ),
** move the cursor to the 'previous' item.
** If there's no prev-item return an xErrorCode != 0
*/
xErrorCode xCollection_Prev(xCollectionCursor* cursor);

/*
**  xCollection_Apply() iterates over all its items, applying the given callback.
**  The callback must return an exit-code (an integer); if this returned code is NOT 0,
**  then the iteration is stopped, and xCollection_Apply returns this value.
**
** In its simplest form the callback receives just the current xCollectionData;
** A simple callback is something like  this

	int myCb(xCollectionData data) {
		..  do something on data  ... / e.g.  print it, change some values,  ...
		return 0;
	}

	xCollection_Apply( myColl, myCb );

** but in general, xCollection_Apply() accepts some extra parameters that are passed back to the callback.
**   e.g. 	xCollection_Apply( myColl, mySearchCb, param1, param2, param3)
	In these cases the callback should have the follow format
	   int myCb(xCollectionData item, va_list args)
	like in the following example:

	 // VERY VERY IMPORTANT: within the calback function, the list of parameters extracted by 'args'
	 // should *exactly* match the optional parameters passed to xCollection_Apply().

	int mySearchCb(xCollectionData item, va_list args ) {
		 // extract the parameters :
		 //   we expect a string (char*)
		 //   and the address of an integer where to store  the result (i.e the number of matches).

		int searchStr = va_arg(args,char*);
		int pCount = va_arg(args, int*);
		if( ... item matches searchKey ... )  ++(*pCount);
		return 0;
	}


	  //  here we add some params for the callback mySearchCb that will be added
	  //  after the standard xCollectionData parameter.
	  //  For this custom callback we add two parameters:
	  //   a string to search for, and a memory address where to store the result
	int count = 0;
	xCollection_Apply( myColl, mySearchCb, "someValue", &count)
**
*/
typedef int (xCollection_ApplyProc)(xCollectionData data, ...);

int vxCollection_Apply(xCollection_ApplyProc* fun, const xCollection* store, va_list args);
int xCollection_Apply(xCollection_ApplyProc* Fun, const xCollection* store, ...);


/*
**  xCollection_Match() iterates over all its items, applying the given callback.
**  The callback must have the form of xCollection_MatchProc, like in the following example:
**
	int mySearchCb(xCollectionData data, void* searckKey) {
        .. cast data and searchKey to some types ... 
		id ( data matches searckKey ) return 1;  // or any value != 0
		return 0;  // no match
	}

**  The callback must return an exit-code (an integer); if this returned code is NOT 0,
**  then the iteration is stopped; xCollection_Match returns this value,
**  AND saves in pCursor (if not NULL) the current xCursor.
**
**  The returned pCursor can then be used for getting the matched data  
**  (by calling xCollection_Value(pCursor))
**  or for removing the data (by calling xCollection_RemoveAt()).
**  Using xCollection_Next/Prev with the returned cursor is possible, but it don't make sense,
**  because items in a xCollection are unpredictably ordered.
**  If you are just interested checking if a matching data exists, then you can pass a NULL value for pCursor.
*/

typedef int (xCollection_MatchProc)(const xCollectionData data, const void* searchKey);
int xCollection_Match(const xCollection* store, xCollection_MatchProc* fun, const void* searchKey, xCollectionCursor* pCursor);


/* === Type-specific macros emulating C++ templates ================================================== */

  // The following procedures do not require type-specific form
  //  they are available both in short form   (e.g.  Collection_Destroy())
  //   and in  type-specific form (e.g. CollectionOf_<TYPE>_Destroy() ), too.
#define Collection_Size(store)   	xCollection_Size(store)
#define Collection_Destroy(store)	xCollection_Destroy(store)
#define Collection_RemoveAt(cursor)	xCollection_RemoveAt(cursor)
#define CollectionCursor	xCollectionCursor


/*
** Pseudo C++ template; define type-specific structs and procedures.
** -----------------------------------------------------------------
**  Macro DEFINE_COLLECTION_OF(TYPE,TYPENAME) defines a type-specific collection 
**  for storing TYPE, named CollectionOf_TYPENAME
**  and generates type-specific functions
**  such as
**  CollectionOf_TYPENAME_New(),  CollectionOf_TYPENAME_Add(), CollectionOf_TYPENAME_First() ..
**
**
**  TYPE should be the one-word C-type stored in xCollection  
**    e.g. (without quotes) "int"  "char *"  "struct XXX*"
**  TYPENAME should be the *id-name* of that type ( string made of only alfanum and underscore) 
**  and it will be used for defining type-specific procedures.
**
**  Example:
**    If we want to collect some "FILE *", since we cannot use "FILE *" as a TYPENAME, 
**    we could name it as "FILE_PTR"  , "FILE" or ..  "FILES"  
**    DEFINE_COLLECTION_OF(FILE *, "FILES")
**     will provide a new set of typedefs and procedures like
**      CollectionOf_FILES
**      CollectionOf_FILES_New()
**      ...
**
*/
#define  DECLARE_COLLECTION_OF(TYPE, TYPENAME)                                                             \
	typedef xCollection  CollectionOf_##TYPENAME;                                                          \
	typedef void (CollectionOf_##TYPENAME##_DestroyProc)(TYPE data);                                       \
                                                                                                           \
	CollectionOf_##TYPENAME* CollectionOf_##TYPENAME##_New(CollectionOf_##TYPENAME##_DestroyProc* fun);    \
	void CollectionOf_##TYPENAME##_Destroy(CollectionOf_##TYPENAME* store);                                \
	void CollectionOf_##TYPENAME##_Reset(CollectionOf_##TYPENAME* store);                                  \
	int CollectionOf_##TYPENAME##_Size(const CollectionOf_##TYPENAME* store);                              \
                                                                                                           \
	TYPE CollectionOf_##TYPENAME##_Value(const xCollectionCursor* cursor);                                 \
                                                                                                           \
	xErrorCode CollectionOf_##TYPENAME##_Add(CollectionOf_##TYPENAME* store, const TYPE data);             \
	xErrorCode CollectionOf_##TYPENAME##_RemoveAt(xCollectionCursor* cursor);                              \
                                                                                                           \
	xErrorCode CollectionOf_##TYPENAME##_First(const CollectionOf_##TYPENAME* store, xCollectionCursor* cursor); \
	xErrorCode CollectionOf_##TYPENAME##_Last(const CollectionOf_##TYPENAME* store, xCollectionCursor* cursor);  \
	xErrorCode CollectionOf_##TYPENAME##_Next(xCollectionCursor* cursor);                                  \
	xErrorCode CollectionOf_##TYPENAME##_Prev(xCollectionCursor* cursor);                                  \
                                                                                                           \
	typedef int (CollectionOf_##TYPENAME##_ApplyProc)(TYPE data, ...);                                     \
	int CollectionOf_##TYPENAME##_Apply(CollectionOf_##TYPENAME##_ApplyProc* fun, const CollectionOf_##TYPENAME* store, ...); \
                                                                                                           \
    typedef int (CollectionOf_##TYPENAME##_MatchProc)(const TYPE data, const void* searchKey);             \
    int CollectionOf_##TYPENAME##_Match(const CollectionOf_##TYPENAME* store, CollectionOf_##TYPENAME##_MatchProc* fun, const void* searchKey, xCollectionCursor* pCursor);  \


#define  DEFINE_COLLECTION_OF(TYPE, TYPENAME)                                                              \
	CollectionOf_##TYPENAME* CollectionOf_##TYPENAME##_New(CollectionOf_##TYPENAME##_DestroyProc* fun) {   \
		return xCollection_New((xCollection_DestroyProc*)fun);                                             \
	}                                                                                                      \
	void CollectionOf_##TYPENAME##_Destroy(CollectionOf_##TYPENAME* store) {                               \
		xCollection_Destroy(store);                                                                        \
	}                                                                                                      \
	void CollectionOf_##TYPENAME##_Reset(CollectionOf_##TYPENAME* store) {                                 \
		xCollection_Reset(store);                                                                          \
	}                                                                                                      \
	int CollectionOf_##TYPENAME##_Size(const CollectionOf_##TYPENAME* store) {                             \
		return xCollection_Size(store);                                                                    \
	}                                                                                                      \
                                                                                                           \
	TYPE CollectionOf_##TYPENAME##_Value(const xCollectionCursor* cursor) {                                \
		return (TYPE)xCollection_Value(cursor);                                                            \
	}                                                                                                      \
                                                                                                           \
	xErrorCode CollectionOf_##TYPENAME##_Add(CollectionOf_##TYPENAME* store, const TYPE data) {            \
		return xCollection_Add(store, (const xCollectionData)data);                                        \
	}                                                                                                      \
	xErrorCode CollectionOf_##TYPENAME##_RemoveAt(xCollectionCursor* cursor) {                             \
		return xCollection_RemoveAt(cursor);                                                               \
	}                                                                                                      \
                                                                                                           \
	xErrorCode CollectionOf_##TYPENAME##_First(const CollectionOf_##TYPENAME* store, xCollectionCursor* cursor) { \
		return xCollection_First(store,cursor);                                                            \
	}                                                                                                      \
	xErrorCode CollectionOf_##TYPENAME##_Last(const CollectionOf_##TYPENAME* store, xCollectionCursor* cursor) { \
		return xCollection_Last(store,cursor);                                                             \
	}                                                                                                      \
	xErrorCode CollectionOf_##TYPENAME##_Next(xCollectionCursor* cursor) {                                 \
		return xCollection_Next(cursor);                                                                   \
	}                                                                                                      \
	xErrorCode CollectionOf_##TYPENAME##_Prev(xCollectionCursor* cursor) {                                 \
		return xCollection_Prev(cursor);                                                                   \
	}                                                                                                      \
                                                                                                           \
	int CollectionOf_##TYPENAME##_Apply(CollectionOf_##TYPENAME##_ApplyProc* fun, const CollectionOf_##TYPENAME* store, ...) { \
		va_list args;                                                                                      \
		va_start(args, store);                                                                             \
		int res = vxCollection_Apply((xCollection_ApplyProc*)fun, (const xCollection*)store, args);        \
		va_end(args);                                                                                      \
		return res;                                                                                        \
	}                                                                                                      \
                                                                                                           \
    int CollectionOf_##TYPENAME##_Match(const CollectionOf_##TYPENAME* store, CollectionOf_##TYPENAME##_MatchProc* fun, const void* searchKey, xCollectionCursor* pCursor) { \
        return xCollection_Match((const xCollection*)store, (xCollection_MatchProc*)fun, searchKey, pCursor);    \
    }                                                                                                      \

#endif
