/*==LICENSE==*

CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, 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/>.

Additional permissions under GNU GPL version 3 section 7

If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.

You can contact Cyan Worlds, Inc. by email legal@cyan.com
 or by snail mail at:
      Cyan Worlds, Inc.
      14617 N Newport Hwy
      Mead, WA   99021

*==LICENSE==*/
#ifndef hsQueue_Defined
#define hsQueue_Defined

#include "hsTypes.h"

template <class T> class hsQueue {
private:
	
	int			fArraySize;
	T			*fArray;
	int 		fHead;		// Index of first element in the queue
	int 		fTail;		// Index of next free spot in the queue 
	int 		fLook;		// Index of look pointer
	hsBool		fFull;		// Is queue full?
	hsBool		fEmpty;		// Is queue empty?
	
	void 		Inc(int *index);
	int 		Inc(int index);

	void 		Dec(int *index);
	int 		Dec(int index);
	
public:
							hsQueue( int size );
							~hsQueue();
	hsBool					Append(const T &newTail);	// Add to end of line
	hsBool					Remove(const T &someElement);	// Find and remove element in the line
	hsBool					Pop(T *headElement);			// Remove and return the head of the line
	hsBool					StartLook(T *headElement);		// Return the head of the line w/out removing it
	hsBool					NextLook(T *nextElement);		// Return the head of the line w/out removing it
	hsBool					IsEmpty(void) { return fEmpty; }
	hsBool					IsFull(void) { return fFull; }
};

//
// Constructor
// Allocate array, init head/tail indices
//
template <class T> hsQueue<T>::hsQueue( int size )
{
	fArraySize = size;
	fArray = TRACKED_NEW T[ size ];
	fHead = -1;
	fTail = -1;
	fLook = -1;
	fEmpty = true;
	fFull = false;
}

//
// Destructor.  free array
//
template <class T> hsQueue<T>::~hsQueue()
{
	delete [] fArray;
}

//
// Wrap index on increment
//
template <class T> void hsQueue<T>::Inc( int *index )
{
	(*index) ++;
	if ((*index) == fArraySize) {
		*index = 0;
	}
}

//
// Wrap index on increment
//
template <class T> int hsQueue<T>::Inc( int index )
{
	(index) ++;
	if ((index) == fArraySize) {
		index = 0;
	}
	return index;
}

//
// Wrap index on decrement
//
template <class T> void hsQueue<T>::Dec( int *index )
{
	(*index) --;
	if ((*index) < 0) {
		*index = fArraySize-1;
	}
}

//
// Wrap index on decrement
//
template <class T> int hsQueue<T>::Dec( int index )
{
	(index) --;
	if ((index) < 0) {
		index = fArraySize-1;
	}
	return index;
}

//
// Add copy of item to the array.
//
template <class T> hsBool hsQueue<T>::Append(const T &thing)
{
	if (fHead == -1 && fTail == -1) {
		// init case
		fHead = 0;
		fTail = 0;
	}

	if (fFull) {
		// Queue is full
		return false;
	}
	
	if ( (fHead<0 || fHead>=fArraySize) ) {
		hsIfDebugMessage( (fHead<0 || fHead>=fArraySize), "Append: Illegal head pointer", fHead);				
	}
	
	hsIfDebugMessage( (fTail<0 || fTail>=fArraySize), "Append: Illegal tail pointer", fTail);

	// Copy
	fArray[fTail] = thing;
	fEmpty = false;

	// increment tail pointer
	Inc(&fTail);
	if (fTail == fHead) {
		fFull = true;
	}
	
	return true;	
}

//
// Get a copy of the head of the array
//
template <class T> hsBool hsQueue<T>::Pop(T *thing)
{
	if (fEmpty) {
		return false;
	}
	
	hsIfDebugMessage( (fHead<0 || fHead>=fArraySize), "Pop: Illegal head pointer", fHead);
	hsIfDebugMessage( (fTail<0 || fTail>=fArraySize), "Pop: Illegal tail pointer", fTail);
			
	// Copy
	*thing = fArray[fHead];
	fFull = false;

	// Increment head pointer
	Inc(&fHead);
	if (fHead == fTail) {
		fEmpty = true;
	}		
	
	return true;
}

//
// Remove item from list
//
template <class T> hsBool hsQueue<T>::Remove(const T &thing)
{
	if (fEmpty) {
		return false;
	}
	
	hsIfDebugMessage( (fHead<0 || fHead>=fArraySize), "Remove: Illegal head pointer", fHead);
	hsIfDebugMessage( (fTail<0 || fTail>=fArraySize), "Remove: Illegal tail pointer", fTail);
	
	// loop through list, find item
	int i = fHead;
	do {
		if (fArray[i] == thing) {
			// Found it - now remove it by sliding everything down 1
			int j=Inc(i);
			while(j!= fTail) {
				if (fLook==j)
					Dec(&fLook);
				fArray[Dec(j)] = fArray[j];
				Inc(&j);
			}
			if (fLook==fTail)
				Dec(&fLook);
			Dec(&fTail);
			if (fTail == fHead) {
				fEmpty = true;
			}
			return true;
		}

	 	Inc(&i);
	 	if (i==fTail) {
	 		return false;
	 	}

	} while(true);
}

//
// Return pointer to first item in list, without popping it.
// Return false if nothing there.
//
template <class T> hsBool hsQueue<T>::StartLook(T *thing)
{
	if (fEmpty) {
		return false;
	}
	
	hsIfDebugMessage( (fHead<0 || fHead>=fArraySize), "StartLook: Illegal head pointer", fHead);
	hsIfDebugMessage( (fTail<0 || fTail>=fArraySize), "StartLook: Illegal tail pointer", fTail);
	
	fLook = fHead;
	*thing = fArray[fLook];

	// inc look pointer 
	Inc(&fLook);
		
	// success
	return true;
}

//
// Return pointer to next item in list, without popping it.  Doesn't change head or tail.
// Should be called immediately after StartLook.
// Return false when at end of list.
//
template <class T> hsBool hsQueue<T>::NextLook(T *thing)
{
	if (fEmpty || fLook == fTail) {
		return false;
	}
	
	hsAssert(fLook != -1, "Must call StartLook first\n");
	hsIfDebugMessage( (fHead<0 || fHead>=fArraySize), "NextLook: Illegal head pointer", fHead);
	hsIfDebugMessage( (fTail<0 || fTail>=fArraySize), "NextLook: Illegal tail pointer", fTail);
	
	// Return copy of item without removing it
	*thing = fArray[fLook];		
	Inc(&fLook);
	return true;
}

/////////////////////////////////////////////////////////////////////////////////////////
//
// Code for threaded message queues - move to another file
//
#ifdef MQUEUE

#include "hsThread.h"
#if HS_BUILD_FOR_UNIX
	#include <mqueue.h>
#endif

class hsListQue {
public:
	struct Elem {
		Elem*	fNext;
	};
private:
	Elem*	fHead;
	Elem*	fTail;
	int		fCount;
public:
				hsListQue();
	virtual		~hsListQue();

	virtual int		Count();
	virtual void	Enqueue(Elem* newItem);
	virtual Elem*	Dequeue();
};

class hsMutexQueue : public hsListQue {
	hsMutex		fMutex;
public:
	hsMutexQueue() {}

	virtual int		Count();
	virtual void	Enqueue(Elem* newItem);
	virtual Elem*	Dequeue();	// will return nil if the queue is empty
};

class hsSemaphoreQueue : public hsMutexQueue {
	hsSemaphore	fSema;
public:
	hsSemaphoreQueue() {}

	virtual void	Enqueue(Elem* newItem);
	virtual Elem*	Dequeue();	// never returns nil, it just waits
};

class hsMsgQueue {
	int	fMaxSize;
#if HS_BUILD_FOR_UNIX
	mqd_t	fMQ;
#else
	class hsPrivateMQ*	fMQ;
	UInt32	fAccess;
#endif
public:
	enum {
		kRead	= 0x0001,
		kWrite	= 0x0002,
		kBlock	= 0x0004
	};

		hsMsgQueue();
	virtual	~hsMsgQueue();

	hsBool	Create(const char name[], int maxSize, UInt32 access);
	hsBool	Open(const char name[], UInt32 access);
	void	Close();

	int	GetMaxSize() const { return fMaxSize; }
	hsBool	Send(const void* data, int size = 0);
	int	Receive(void* data);	// returns actual size or 0

	static void	Delete(const char name[]);
};
#endif // MQUEUE

#endif