// -----------------------------------------------------------------------------
// -- Header file IDOUBLELIST.H 	indirect double list container            --
// -- Writed by David Rouchet                                                 --
// -----------------------------------------------------------------------------

#ifndef __IDOUBLELIST_H
#define __IDOUBLELIST_H

#include <windows.h>
// Si cette classe doit être utilisée en multithreading
// Il est recommandé d'enlever la mise en commentaire
// de la ligne ci-dessous.

//#define __USECRITICALSECTION

// -----------------------------------------------------------------------------
// ---------------- Declaration Class Node --------------------------------------
// -----------------------------------------------------------------------------

template <class T> class Node
{
 public :

	Node	    *Previous;
	Node	    *Next;
	T		    *lpObject;

	Node();
	Node(T* lpObject);

};

// -----------------------------------------------------------------------------
// ---------------- Declaration Class MIDoubleList ----------------------------
// -----------------------------------------------------------------------------
template <class T> class MIDoubleListIterator;

template <class T> class MIDoubleList
{
 public :

    friend MIDoubleListIterator<T>;

 private :

#ifdef __USECRITICALSECTION
    CRITICAL_SECTION        CriticalSection;
#endif

	Node<T>	*Next;
	Node<T>	*Previous;
	Node<T>	*Current;
	Node<T>	*First;
	Node<T>	*Last;
        
	DWORD	NumberOfObject;

    MIDoubleListIterator<T> *ForIter;
    MIDoubleListIterator<T> *FirstIter;
    MIDoubleListIterator<T> *LastIter;

 public :


	MIDoubleList();
	~MIDoubleList();

	T*		GoHome();
	T*		GoEnd();
	T*		GoNext();
	T*		GoPrevious();
	T*		GoTo(T *Here);
	T*		Delete(T *ObjectToDelete = NULL);
	T*		Add(T *NewObject = NULL);
	T*		InsertAfter(T *NewObject = NULL);
	T*		InsertBefore(T *NewObject = NULL);
	T*		Replace(T *Here = NULL, T *NewObject = NULL);
	T*		GetCurrent();
	int	    IsFirst(T *Here);
	int	    IsLast(T *Here);
    T*      FirstThat(int(*CondFunc)(const T& action,void* arg),void* arg) ;
    T*      LastThat(int(*CondFunc)(const T& action,void* arg),void* arg);
	void	ForEach(void(*IterFunc)(T& lpObject,void *arg ),void*arg);
	DWORD	GetItemsInContainer(){return NumberOfObject;}
    void    FreeAllObject();
};

// -----------------------------------------------------------------------------
// ---------------- Definition Class MIDoubleListIterator ---------------------
// -----------------------------------------------------------------------------



template <class T> class MIDoubleListIterator
{

public:

    MIDoubleListIterator( MIDoubleList<T>* );

    const T*    Current();
    const T*    operator ++ ();
    const T*    operator -- ();
    const T*    operator [] (int iIndex);
    void        Restart();

    const T*    Home();
    const T*    Next();
    const T*    Previous();
    const T*    End();

private:

    MIDoubleList<T>          *List; // Pointeur sur la liste
    Node<T>                    *Cur;  // Pointeur sur le noeud courant
};


// -----------------------------------------------------------------------------
// ---------------- Definition Class MIDoubleList -----------------------------
// -----------------------------------------------------------------------------

template <class T>
MIDoubleList<T>::MIDoubleList()
{
#ifdef __USECRITICALSECTION
    ::InitializeCriticalSection(&CriticalSection);
#endif

	Next = Previous = Current = First = Last = NULL;
	NumberOfObject = 0;
    ForIter     = new MIDoubleListIterator<T>(this);
    FirstIter   = new MIDoubleListIterator<T>(this);
    LastIter    = new MIDoubleListIterator<T>(this);
}

template <class T>
MIDoubleList<T>::~MIDoubleList()
{
#ifdef __USECRITICALSECTION
    ::DeleteCriticalSection(&CriticalSection);
#endif

	Node<T> *NodeToDelete;

	if (NumberOfObject != 0) {
		GoHome();
		while (Current->Next != NULL) {
			NodeToDelete = Current;
			Current = Current->Next;
//			if (NodeToDelete->lpObject != NULL) delete NodeToDelete->lpObject;
			delete NodeToDelete;
         NumberOfObject--;
		}
	}
    delete ForIter;
    delete FirstIter;
    delete LastIter;
}

template <class T>
void MIDoubleList<T>::ForEach(void(*IterFunc)(T& action,void* arg),void* arg)
{
	T* Each;
	Each = (T*)ForIter->Home();
	while (Each != NULL) {
		IterFunc(*Each, arg);
		Each = (T*)ForIter->Next();
	}
}

template <class T>
T* MIDoubleList<T>::FirstThat(int(*CondFunc)(const T& action,void* arg),void* arg)
{

	T* Each;
	Each = (T*)FirstIter->Home();
	while (Each != NULL) {
		if (CondFunc((const T&)(*Each), arg)) return Each;
		Each = (T*)FirstIter->Next();
	}
    return NULL;
}

template <class T>
T* MIDoubleList<T>::LastThat(int(*CondFunc)(const T& action,void* arg),void* arg)
{
	T* Each;
	Each = (T*)LastIter->End();
	while (Each != NULL) {
		if (CondFunc((const T&)(*Each), arg)) return Each;
		Each = (T*)LastIter->Previous();
	}
    return NULL;

}

template <class T>
T* MIDoubleList<T>::GetCurrent()
{
	if (Current != NULL) return Current->lpObject;
	else return NULL;
}

template <class T>
T* MIDoubleList<T>::GoHome()
{
    T* lpObject;
#ifdef __USECRITICALSECTION
    ::EnterCriticalSection(&CriticalSection);
#endif

	Current = First;
	if (Current != NULL) lpObject = Current->lpObject;
	else lpObject = NULL;

#ifdef __USECRITICALSECTION
        ::LeaveCriticalSection(&CriticalSection);
#endif
    return lpObject;
}

template <class T>
T* MIDoubleList<T>::GoEnd()
{
    T* lpObject;

#ifdef __USECRITICALSECTION
    ::EnterCriticalSection(&CriticalSection);
#endif

	Current = Last;
	if (Current != NULL) lpObject = Current->lpObject;
	else lpObject = NULL;

#ifdef __USECRITICALSECTION
        ::LeaveCriticalSection(&CriticalSection);
#endif
    return lpObject;
}

template <class T>
T* MIDoubleList<T>::Add(T* NewObject)
{
    T* lpObject = NULL;

#ifdef __USECRITICALSECTION
    ::EnterCriticalSection(&CriticalSection);
#endif

	if (NewObject != NULL) {
		Node<T> *NewNode = new Node<T>(NewObject);
		if (NewNode != NULL) {
			if (Last == NULL) {
				Last 		        = NewNode;
				First   	        = NewNode;
				Current 	        = NewNode;
			} else {
				Last->Next 			= NewNode;  		// Lien Dernier avec Nouveau
				NewNode->Previous 	= Last;  		// Lien Nouveau Avec Dernier
				Last 				= NewNode;  		// Le Nouveau devient le Dernier;
				Current 			= Last; 		// On Pointe sur le Dernier;
			}
			NumberOfObject++;
			if (Current != NULL) lpObject = Current->lpObject;
			else lpObject = NULL;
		}
	 }

#ifdef __USECRITICALSECTION
        ::LeaveCriticalSection(&CriticalSection);
#endif
	 return lpObject;
}

template <class T>
T* MIDoubleList<T>::InsertAfter(T* NewObject)
{
    T* lpObject = NULL;

#ifdef __USECRITICALSECTION
    ::EnterCriticalSection(&CriticalSection);
#endif

	if (NewObject != NULL) {
		Node<T> *NewNode = new Node<T>(NewObject);
		if (NewNode != NULL) {
			if (Current == NULL) {
				delete NewNode;
				lpObject = Add(NewObject);
			} else {
				NewNode->Next = Current->Next;
				NewNode->Previous = Current;
				if (Current->Next != NULL) Current->Next->Previous = NewNode;
				Current->Next = NewNode;
				if (Current == Last) Last = NewNode;
				Current = NewNode; 			// On Pointe sur le Nouveau;
				NumberOfObject++;
				if (Current != NULL) lpObject = Current->lpObject;
				else lpObject = NULL;
			 }
		 }
	}

#ifdef __USECRITICALSECTION
    ::LeaveCriticalSection(&CriticalSection);
#endif

	return lpObject;
}

template <class T>
T* MIDoubleList<T>::InsertBefore(T* NewObject)
{
    T* lpObject = NULL;

#ifdef __USECRITICALSECTION
    ::EnterCriticalSection(&CriticalSection);
#endif

	if (NewObject != NULL) {
		Node<T> *NewNode = new Node<T>(NewObject);
		if (NewNode != NULL) {
			if (Current == NULL) {
				delete NewNode;
				lpObject = Add(NewObject);
			} else {
				NewNode->Previous = Current->Previous;
				NewNode->Next = Current;
				if (Current->Previous != NULL) Current->Previous->Next = NewNode;
				Current->Previous = NewNode;
				if (Current == First) First = NewNode;
				Current = NewNode; 			// On Pointe sur le Nouveau;
				NumberOfObject++;
				if (Current != NULL) lpObject = Current->lpObject;
				else lpObject = NULL;
			 }
		 }
	}

#ifdef __USECRITICALSECTION
    ::LeaveCriticalSection(&CriticalSection);
#endif

	return lpObject;
}

template <class T>
T* MIDoubleList<T>::Replace(T *Here, T *NewObject)
{
    T* lpObject = NULL;

#ifdef __USECRITICALSECTION
    ::EnterCriticalSection(&CriticalSection);
#endif

	if (NewObject != NULL) {
		if (GoTo(Here) != NULL) {					// Current pointe sur le noeud NewObject
			Current->lpObject = NewObject;      // l'ancien devient le nouveau
			lpObject = Current->lpObject;           // on retourne le nouveau
		}
	}
#ifdef __USECRITICALSECTION
    ::LeaveCriticalSection(&CriticalSection);
#endif
	return lpObject;
}

template <class T>
T* MIDoubleList<T>::Delete(T* ObjectToDelete)
{
    T* lpObject = NULL;

	Node<T> *NodeToDelete;

#ifdef __USECRITICALSECTION
    ::EnterCriticalSection(&CriticalSection);
#endif

	if (ObjectToDelete != NULL) {
		if (GoTo(ObjectToDelete) != NULL) {	// Current pointe sur le noeud ObjectToDelete
			NodeToDelete = Current;				// On memorise le courant

			if (NodeToDelete->Previous != NULL) { // No first of list to delete
				NodeToDelete->Previous->Next = NodeToDelete->Next;
			} else { // Firt of list to delete
				First = NodeToDelete->Next;
			}

			if (NodeToDelete->Next != NULL) {  // No last of list to delete
				NodeToDelete->Next->Previous = NodeToDelete->Previous;
			} else {
				Last = NodeToDelete->Previous;
			}

			Current = NodeToDelete->Next;

			NumberOfObject--;
			delete NodeToDelete;
		}
	}

	if (Current != NULL) lpObject = Current->lpObject;

#ifdef __USECRITICALSECTION
    ::LeaveCriticalSection(&CriticalSection);
#endif

	return lpObject;
}

template <class T>
T* MIDoubleList<T>::GoNext()
{
    T* lpObject = NULL;

#ifdef __USECRITICALSECTION
    ::EnterCriticalSection(&CriticalSection);
#endif

	if (Current != Last) {
         if (Current) {
    		Current = Current->Next; // On Pointe sur le suivant
	    	if (Current != NULL) lpObject = Current->lpObject;
		    else lpObject = NULL;
         } else lpObject = NULL;
    }

#ifdef __USECRITICALSECTION
    ::LeaveCriticalSection(&CriticalSection);
#endif

	return lpObject;
}

template <class T>
T* MIDoubleList<T>::GoPrevious()
{
    T* lpObject = NULL;

#ifdef __USECRITICALSECTION
    ::EnterCriticalSection(&CriticalSection);
#endif

	if (Current != First) {
        if (Current) {
    		Current = Current->Previous; // On Pointe sur le Precedent
	    	if (Current != NULL) lpObject = Current->lpObject;
		    else lpObject = NULL;
        } else lpObject = NULL;
	}

#ifdef __USECRITICALSECTION
    ::LeaveCriticalSection(&CriticalSection);
#endif

    return lpObject;
}

template <class T>
T* MIDoubleList<T>::GoTo(T *Here)
{
    T* lpObject = NULL;

#ifdef __USECRITICALSECTION
    ::EnterCriticalSection(&CriticalSection);
#endif

	if (Here != NULL) {
		GoHome();
		while (Current != NULL) {
			if (Current->lpObject == Here) break;
			GoNext();
		}
		if (Current != NULL) lpObject = Current->lpObject;
		else lpObject = NULL;
    }

#ifdef __USECRITICALSECTION
    ::LeaveCriticalSection(&CriticalSection);
#endif

    return lpObject;
}


template <class T>
int MIDoubleList<T>::IsFirst(T *Here)
{
	if (Here == First->lpObject) return true;
	return false;
}

template <class T>
int MIDoubleList<T>::IsLast(T *Here)
{
	if (Here == Last->lpObject) return true;
	return false;
}

template <class T>
void MIDoubleList<T>::FreeAllObject()
{
#ifdef __USECRITICALSECTION
    ::EnterCriticalSection(&CriticalSection);
#endif

	GoHome();
	while (Current != NULL) {
    	if (Current->lpObject) delete Current->lpObject;
        if (Current->Next) Current->Next->Previous = NULL;
        First = Current->Next;
        delete Current;
        Current = First;
    	NumberOfObject--;
//		GoHome();
	}
	Next = Previous = Current = First = Last = NULL;
	NumberOfObject = 0;

#ifdef __USECRITICALSECTION
    ::LeaveCriticalSection(&CriticalSection);
#endif
}

// -----------------------------------------------------------------------------
// ---------------- Definition Class Node ---------------------------------------
// -----------------------------------------------------------------------------


template <class T>
Node<T>::Node()
{
	lpObject 	= NULL;
	Previous		= NULL;
	Next			= NULL;
}

template <class T>
Node<T>::Node(T* lpobject)
{
	lpObject 	= lpobject;
	Previous		= NULL;
	Next			= NULL;
}


// -----------------------------------------------------------------------------
// ---------------- Definition Class MIDoubleListIterator ---------------------
// -----------------------------------------------------------------------------

template <class T>MIDoubleListIterator<T>::MIDoubleListIterator( MIDoubleList<T>* lplist )
{
    List = lplist;
    Restart();
}

template <class T>
const T* MIDoubleListIterator<T>::Current()
{
    if (Cur) return Cur->lpObject;
    return NULL;
}

template <class T>
const T* MIDoubleListIterator<T>::operator ++ ()
{
//        PRECONDITION( Cur->Next != &(List->Tail) );
    if (Cur==NULL) return NULL;
    Cur = Cur->Next;
    if (Cur) return Cur->lpObject;
    return NULL;
}

template <class T>
const T* MIDoubleListIterator<T>::operator -- ()
{
//        PRECONDITION( Cur->Next != &(List->Tail) );
    if (Cur==NULL) return NULL;
    Cur = Cur->Previous;
    if (Cur) return Cur->lpObject;
    return NULL;
}

template <class T>
const T* MIDoubleListIterator<T>::operator [] (int iIndex)
{
    if (iIndex>=(int)List->GetItemsInContainer() || (iIndex<0)) return NULL;
    Restart();
    while ((Cur) && (iIndex--)) {
        Cur = Cur->Next;
    }
    if (Cur) return Cur->lpObject;
    return NULL;
}

template <class T>
void MIDoubleListIterator<T>::Restart()
{
    if (List->First) Cur = List->First;
    else Cur = NULL;
}

template <class T>
const T* MIDoubleListIterator<T>::Home()
{
    Restart();
    if (Cur) return Cur->lpObject;
    return NULL;
}

template <class T>
const T* MIDoubleListIterator<T>::Next()
{
    if (Cur==NULL) return NULL;
    Cur = Cur->Next;
    if (Cur) return Cur->lpObject;
    return NULL;
}

template <class T>
const T* MIDoubleListIterator<T>::Previous()
{
    if (Cur==NULL) return NULL;
	if (Cur != List->First) {
		Cur = Cur->Previous; // On Pointe sur le Precedent
		if (Cur != NULL) return Cur->lpObject;
		else return NULL;
	} else return NULL;
/*
    if (Cur==NULL) return NULL;
    Cur = Cur->Previous;
    if (Cur) return Cur->lpObject;
    return NULL;
    */
}

template <class T>
const T* MIDoubleListIterator<T>::End()
{
/*
    if (List->Last)
    else Cur = NULL;
*/
    Cur = List->Last;
    if (Cur) return Cur->lpObject;
    return NULL;
}
#endif


