/*****************************************************************
|
|   Neptune - Threads
|
| Copyright (c) 2002-2008, Axiomatic Systems, LLC.
| All rights reserved.
|
| Redistribution and use in source and binary forms, with or without
| modification, are permitted provided that the following conditions are met:
|     * Redistributions of source code must retain the above copyright
|       notice, this list of conditions and the following disclaimer.
|     * Redistributions in binary form must reproduce the above copyright
|       notice, this list of conditions and the following disclaimer in the
|       documentation and/or other materials provided with the distribution.
|     * Neither the name of Axiomatic Systems nor the
|       names of its contributors may be used to endorse or promote products
|       derived from this software without specific prior written permission.
|
| THIS SOFTWARE IS PROVIDED BY AXIOMATIC SYSTEMS ''AS IS'' AND ANY
| EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
| WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
| DISCLAIMED. IN NO EVENT SHALL AXIOMATIC SYSTEMS BE LIABLE FOR ANY
| DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
| (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
| LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
| ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
| (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
| SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
 ****************************************************************/

#ifndef _NPT_THREADS_H_
#define _NPT_THREADS_H_

/*----------------------------------------------------------------------
|   includes
+---------------------------------------------------------------------*/
#include "NptTypes.h"
#include "NptConstants.h"
#include "NptInterfaces.h"

/*----------------------------------------------------------------------
|   error codes
+---------------------------------------------------------------------*/
const int NPT_ERROR_CALLBACK_HANDLER_SHUTDOWN = NPT_ERROR_BASE_THREADS-0;
const int NPT_ERROR_CALLBACK_NOTHING_PENDING  = NPT_ERROR_BASE_THREADS-1;

/*----------------------------------------------------------------------
|   constants
+---------------------------------------------------------------------*/
const int NPT_THREAD_PRIORITY_MIN           = -15;
const int NPT_THREAD_PRIORITY_IDLE          = -15;
const int NPT_THREAD_PRIORITY_LOWEST        =  -2;
const int NPT_THREAD_PRIORITY_BELOW_NORMAL  =  -1;
const int NPT_THREAD_PRIORITY_NORMAL        =   0;
const int NPT_THREAD_PRIORITY_ABOVE_NORMAL  =   1;
const int NPT_THREAD_PRIORITY_HIGHEST       =   2;
const int NPT_THREAD_PRIORITY_TIME_CRITICAL =  15;
const int NPT_THREAD_PRIORITY_MAX           =  15;

/*----------------------------------------------------------------------
|   NPT_MutexInterface
+---------------------------------------------------------------------*/
class NPT_MutexInterface
{
 public:
    // methods
    virtual           ~NPT_MutexInterface() {}
    virtual NPT_Result Lock()   = 0;
    virtual NPT_Result Unlock() = 0;
};

/*----------------------------------------------------------------------
|   NPT_Mutex
+---------------------------------------------------------------------*/
class NPT_Mutex : public NPT_MutexInterface
{
 public:
    // methods
               NPT_Mutex(bool recursive = false);
              ~NPT_Mutex() override { delete m_Delegate; }
    NPT_Result Lock() override   { return m_Delegate->Lock();   }
    NPT_Result Unlock() override { return m_Delegate->Unlock(); }

 private:
    // members
    NPT_MutexInterface* m_Delegate;
};

/*----------------------------------------------------------------------
|   NPT_AutoLock
+---------------------------------------------------------------------*/
class NPT_AutoLock
{
 public:
    // methods
     NPT_AutoLock(NPT_Mutex &mutex) : m_Mutex(mutex)   {
        m_Mutex.Lock();
    }
    ~NPT_AutoLock() {
        m_Mutex.Unlock(); 
    }
        
 private:
    // members
    NPT_Mutex& m_Mutex;
};

/*----------------------------------------------------------------------
|   NPT_Lock
+---------------------------------------------------------------------*/
template <typename T> 
class NPT_Lock : public T,
                 public NPT_Mutex
{
};

/*----------------------------------------------------------------------
|   NPT_SingletonLock
+---------------------------------------------------------------------*/
class NPT_SingletonLock
{
public:
    static NPT_Mutex& GetInstance() {
        return Instance;
    }
    
private:
    static NPT_Mutex Instance;
};

/*----------------------------------------------------------------------
|   NPT_SharedVariableInterface
+---------------------------------------------------------------------*/
class NPT_SharedVariableInterface
{
 public:
    // methods
    virtual           ~NPT_SharedVariableInterface() {}
    virtual void       SetValue(int value)= 0;
    virtual int        GetValue()         = 0;
    virtual NPT_Result WaitUntilEquals(int value, NPT_Timeout timeout = NPT_TIMEOUT_INFINITE) = 0;
    virtual NPT_Result WaitWhileEquals(int value, NPT_Timeout timeout = NPT_TIMEOUT_INFINITE) = 0;
};

/*----------------------------------------------------------------------
|   NPT_SharedVariable
+---------------------------------------------------------------------*/
class NPT_SharedVariable : public NPT_SharedVariableInterface
{
 public:
    // methods
               NPT_SharedVariable(int value = 0);
              ~NPT_SharedVariable() override { delete m_Delegate; }
    void SetValue(int value) override { 
        m_Delegate->SetValue(value); 
    }
    int GetValue() override { 
        return m_Delegate->GetValue(); 
    }
    NPT_Result WaitUntilEquals(int value, NPT_Timeout timeout = NPT_TIMEOUT_INFINITE) override { 
        return m_Delegate->WaitUntilEquals(value, timeout); 
    }
    NPT_Result WaitWhileEquals(int value, NPT_Timeout timeout = NPT_TIMEOUT_INFINITE) override { 
        return m_Delegate->WaitWhileEquals(value, timeout); 
    }

 private:
    // members
    NPT_SharedVariableInterface* m_Delegate;
};

/*----------------------------------------------------------------------
|   NPT_AtomicVariableInterface
+---------------------------------------------------------------------*/
class NPT_AtomicVariableInterface
{
 public:
    // methods
    virtual      ~NPT_AtomicVariableInterface() {}
    virtual  int  Increment() = 0;
    virtual  int  Decrement() = 0;
    virtual  int  GetValue()  = 0;
    virtual  void SetValue(int value)  = 0;
};

/*----------------------------------------------------------------------
|   NPT_AtomicVariable
+---------------------------------------------------------------------*/
class NPT_AtomicVariable : public NPT_AtomicVariableInterface
{
 public:
    // methods
         NPT_AtomicVariable(int value = 0);
        ~NPT_AtomicVariable() override { delete m_Delegate;             }
    int  Increment() override          { return m_Delegate->Increment();}
    int  Decrement() override          { return m_Delegate->Decrement();}
    void SetValue(int value) override  { m_Delegate->SetValue(value);   }
    int  GetValue() override           { return m_Delegate->GetValue(); }

 private:
    // members
    NPT_AtomicVariableInterface* m_Delegate;
};

/*----------------------------------------------------------------------
|   NPT_Runnable
+---------------------------------------------------------------------*/
class NPT_Runnable
{
public:
    virtual ~NPT_Runnable() {}  
    virtual void Run() = 0;
};

/*----------------------------------------------------------------------
|   NPT_ThreadInterface
+---------------------------------------------------------------------*/
class NPT_ThreadInterface: public NPT_Runnable, public NPT_Interruptible
{
 public:
    // methods
              ~NPT_ThreadInterface() override {}
    virtual NPT_Result Start() = 0;
    virtual NPT_Result Wait(NPT_Timeout timeout = NPT_TIMEOUT_INFINITE) = 0;
    virtual NPT_Result SetPriority(int /*priority*/) { return NPT_SUCCESS; } 
    virtual NPT_Result CancelBlockerSocket() = 0;
    virtual NPT_Result GetPriority(int& priority) = 0;
};

/*----------------------------------------------------------------------
|   NPT_Thread
+---------------------------------------------------------------------*/
class NPT_Thread : public NPT_ThreadInterface
{
 public:
    // types
    typedef NPT_UInt64 ThreadId;

    // class methods
    static ThreadId   GetCurrentThreadId();
    static NPT_Result SetCurrentThreadPriority(int priority);
    static NPT_Result GetCurrentThreadPriority(int& priority);

    // methods
    explicit NPT_Thread(bool detached = false);
    explicit NPT_Thread(NPT_Runnable& target, bool detached = false);
   ~NPT_Thread() override { delete m_Delegate; }

    // cancel any socket that this thread may be waiting for
    NPT_Result CancelBlockerSocket() override { return m_Delegate->CancelBlockerSocket(); }

    // NPT_ThreadInterface methods
    NPT_Result Start() override { 
        return m_Delegate->Start(); 
    } 
    NPT_Result Wait(NPT_Timeout timeout = NPT_TIMEOUT_INFINITE) override  { 
        return m_Delegate->Wait(timeout);  
    }
    NPT_Result SetPriority(int priority) override {
        return m_Delegate->SetPriority(priority);
    }    
    NPT_Result GetPriority(int& priority) override {
        return m_Delegate->GetPriority(priority);
    }

    // NPT_Runnable methods
    void Run() override {}

    // NPT_Interruptible methods
    NPT_Result Interrupt() override { return m_Delegate->Interrupt(); }

 private:
    // members
    NPT_ThreadInterface* m_Delegate;
};


/*----------------------------------------------------------------------
|   NPT_ThreadCallbackReceiver
+---------------------------------------------------------------------*/
class NPT_ThreadCallbackReceiver
{
public:
    virtual ~NPT_ThreadCallbackReceiver() {}
    virtual void OnCallback(void* args) = 0;
};

/*----------------------------------------------------------------------
|   NPT_ThreadCallbackSlot
+---------------------------------------------------------------------*/
class NPT_ThreadCallbackSlot
{
public:
    // types
    class NotificationHelper {
    public:
        virtual ~NotificationHelper() {};
        virtual void Notify(void) = 0;
    };

    // constructor
    NPT_ThreadCallbackSlot();

    // methods
    NPT_Result ReceiveCallback(NPT_ThreadCallbackReceiver& receiver, NPT_Timeout timeout = 0);
    NPT_Result SendCallback(void* args);
    NPT_Result SetNotificationHelper(NotificationHelper* helper);
    NPT_Result Shutdown();

protected:
    // members
    volatile void*      m_CallbackArgs;
    volatile bool       m_Shutdown;
    NPT_SharedVariable  m_Pending;
    NPT_SharedVariable  m_Ack;
    NPT_Mutex           m_ReadLock;
    NPT_Mutex           m_WriteLock;
    NotificationHelper* m_NotificationHelper;
};

#endif // _NPT_THREADS_H_
