/*==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