/*
 * Thread API.
 *
 * Author: Matthew William Coan
 * Date: Wed Aug  4 20:01:07 EDT 2010
 *
 */

#ifndef _THREAD_H
#define _THREAD_H

#include <windows.h>
#include <list>
#include <iostream>

namespace thread_tools {

using namespace std;

extern DWORD WINAPI thread_function(PVOID arg);
class Thread {
   HANDLE _thread0;
   DWORD _id;

public:
   Thread(HANDLE hThread) {
      _thread0 = hThread;
   }
   Thread() { _thread0 = 0; }
   Thread(const Thread & th) { _thread0 = th._thread0; }
   virtual ~Thread() { 
      if(_thread0) {
         CloseHandle(_thread0); 
      }
   }

   Thread & operator=(const Thread & arg) {
      _thread0 = arg._thread0;
      return *this;
   }

   void start() { 
      _thread0 = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)thread_function, (void*)this, 0, &_id);
   } 

   void join(DWORD mills = INFINITE) {
      if(_thread0 != 0) {
         WaitForSingleObject(_thread0, mills);
      }
   }
   
   void suspend() {
      SuspendThread(_thread0);
   }

   void resume() {
      ResumeThread(_thread0);
   }

   void stop() {
      SuspendThread(_thread0);
      CloseHandle(_thread0);
      _thread0 = 0;
   }

   int status() {
      DWORD ret = 0;
      GetExitCodeThread(_thread0, &ret);
      return (int)ret;
   }

   DWORD get_id() {
      return _id;
   }

   virtual void run() { }
};

/*
class Mutex {
   HANDLE _mutex;

public:
   Mutex() { _mutex = CreateMutex(NULL, FALSE, NULL); }
   Mutex(const Mutex & m) { _mutex = m._mutex; }
   ~Mutex() { CloseHandle(_mutex); }

   Mutex & operator=(const Mutex & arg) { _mutex = arg._mutex; return *this; }

   void lock() { WaitForSingleObject(_mutex, INFINITE); }
   void unlock() { ReleaseMutex(_mutex); }
   HANDLE get_mutex() { return _mutex; }
};
*/

class Mutex {
   CRITICAL_SECTION _mutex;

public:
   Mutex() { InitializeCriticalSection(&_mutex); }
   Mutex(const Mutex & m) { _mutex = m._mutex; }
   ~Mutex() { DeleteCriticalSection(&_mutex); }

   Mutex & operator=(const Mutex & arg) { _mutex = arg._mutex; return *this; }

   void lock() { EnterCriticalSection(&_mutex); }
   void unlock() { LeaveCriticalSection(&_mutex); }
   CRITICAL_SECTION * get_mutex() { return &_mutex; }
};

class Condition {
   CONDITION_VARIABLE _cond;

public:
   Condition() {  InitializeConditionVariable(&_cond); }
   Condition(const Condition & cp) { _cond = cp._cond; }
   ~Condition() { }

   Condition & operator=(const Condition& right) {
      _cond = right._cond;
      return *this;
   }

   void wait(Mutex & mutex) { SleepConditionVariableCS(&_cond, mutex.get_mutex(), INFINITE); }
   void wait(Mutex & mutex, const long mills) { SleepConditionVariableCS(&_cond, mutex.get_mutex(), mills); }
   void notify() { WakeConditionVariable(&_cond); }
   void notify_all() { WakeAllConditionVariable(&_cond); }

   CONDITION_VARIABLE * get_cond() { return &_cond; }
};

class ReadWriteLock {
   Mutex _mutex;
   Condition _cond;
   int _reader;
   int _writer;
   
public:
   enum last_type {
      LAST_EX,
      LAST_SH,
      LAST_NIL,
   };

private:
   last_type last;

public:
   ReadWriteLock() { _reader = 0U; _writer = 0U; last = LAST_NIL; }
   ReadWriteLock(const ReadWriteLock & cp) { 
   _reader = cp._reader; _writer = cp._writer; _mutex = cp._mutex; _cond = cp._cond; last = LAST_NIL; }
   ~ReadWriteLock() { }

   ReadWriteLock & operator=(const ReadWriteLock & rw) {
      _mutex = rw._mutex;
      _cond = rw._cond;
      _reader = rw._reader;
      _writer = rw._writer;
      last = rw.last;
      return *this;
   }

   void read_lock();
   void read_unlock();

   void write_lock();
   void write_unlock();

   last_type get_last() { return last; }
};

inline
void 
ReadWriteLock::read_lock() 
{ 
   _mutex.lock(); 

   while(_writer)
      _cond.wait(_mutex);

   _reader++; 

   last = LAST_SH;

   _mutex.unlock(); 
}

inline
void 
ReadWriteLock::read_unlock() 
{
   _mutex.lock();

   _reader--; 

   _cond.notify_all();

   last = LAST_NIL;

   _mutex.unlock(); 
}
   
inline
void 
ReadWriteLock::write_lock() 
{ 
   _mutex.lock(); 

   while(_reader)
      _cond.wait(_mutex);

   _writer++;

   last = LAST_EX;

   _mutex.unlock();
}

inline
void 
ReadWriteLock::write_unlock() 
{ 
   _mutex.lock();

   _writer--;

   _cond.notify_all();

   last = LAST_NIL;

   _mutex.unlock(); 
}

template< class T > 
class Queue {
public:
   typedef list< T > queue_list_type;
   struct queue_exception { };

private:
   Mutex _mutex;
   Condition _cond;
   queue_list_type _queue;
   bool _is_running;

public:
   Queue() { _is_running = true; }
   Queue(const Queue & cp) { 
      _mutex = cp._mutex;
      _cond = cp._cond;
      _queue = cp._queue;
      _is_running = cp._is_running;
   }
   ~Queue() { }
   Queue & operator=(const Queue & right) {
      _mutex = right._mutex;
      _cond = right._cond;
      _queue = right._quque;
      _is_running = right._is_running;
      return *this;
   }

   void put(const T & arg) {
      _mutex.lock();
      _queue.push_back(arg);
      _cond.notify_all(); 
      _mutex.unlock();
   }

   T get() {
     T temp;
     _mutex.lock();
     while(_queue.size() == 0 && _is_running) {
        _cond.wait(_mutex);
     }
     if(_is_running) {
        temp = _queue.front();
        _queue.pop_front();
     }
     else {
        _mutex.unlock();
        throw queue_exception();
     }
     _mutex.unlock();
     return temp;
   }

   bool is_running() { 
      _mutex.lock();
      bool flag = _is_running;
      _mutex.unlock();
      return flag; 
   }

   void shutdown() {
      _mutex.lock();
      _is_running = false;
      _cond.notify_all();
      _mutex.unlock();
   }
};
}

#endif /* _THREAD_H */
