/*********************************************************
** Copyright (c) 2005
** University of Washington
** Licensed under the terms set forth by University of
** Washington. If you did not sign such a license, you
** are using this software/code illegally and you do not
** have permission to use, modify, or redistribute
** this or any files in this software package.
**
** File: ProgressThread.h
**
**********************************************************/
#ifndef PROGRESSTHREAD_H
#define PROGRESSTHREAD_H

#include "ProgressThreadDef.h"

#include <map>
#include <string>
using namespace std;

#include "Clock.h"

class CMutex
{
 public:
  CMutex();
  ~CMutex();

  int Lock();
  int Unlock();
 private:
#ifdef WIN32
  HANDLE m_mutex;
  DWORD m_err;
#else
  pthread_mutex_t m_mutex;
#endif
};


void CleanupProgressThreadForTermination(void *arg);
void UpdateProgressThreadProgress(void* arg, double max, double current, int msgid);

class CProgressThread 
{
 public:
  CProgressThread() : m_pArg(NULL), m_pReturnData(NULL), m_returnStatus(0), 
    m_bFinished(false), m_bCanceled(false), m_tid((THREAD_ID_T)0), m_pData(NULL), 
    m_startTime((CLOCK_T)0), m_endTime((CLOCK_T)0) {}
  virtual ~CProgressThread() {}

  inline  int SetArg(void* pArg) { 
    return SetProperty((void**)&m_pArg, (void*)pArg);
  }
  inline  void* GetArg() { return m_pArg; }

  inline  int SetReturnData(void* pData) { 
    return SetProperty((void**)&m_pData, (void*)pData);
  }
  inline  void* GetReturnData() { return m_pData; }

  int SetReturnData(void* pData, int returnDataSize);

  inline  int GetReturnDataSize() { return m_returnDataSize; }

  inline  int SetReturnStatus(int status) { 
    return SetProperty((void**)&m_returnStatus, (void*)status);
  }
  inline  int GetReturnStatus() { return m_returnStatus; }

  // Error info, in case the 
  inline string GetErrorMessage() { return m_sErrMsg; }
  inline int SetErrorMessage(const char* pMsg) {
    int result = m_mutex.Lock();
    if(0 == result)
      {
	m_sErrMsg = string(pMsg);
	result = m_mutex.Unlock();
      }
    return result;
  }

  inline int SetErrorMessage(string sMsg) {
    int result = m_mutex.Lock();
    if(0 == result)
      {
	m_sErrMsg = string(sMsg);
	result = m_mutex.Unlock();
      }
    return result;
  }

  inline  int SetIsFinished(bool finished) { 
    return SetProperty((void**)&m_bFinished, (void*)finished); }
  inline  bool GetIsFinished() { return m_bFinished; }

  inline  int SetIsStarted(bool finished) { 
    return SetProperty((void**)&m_bStarted, (void*)finished); }
  inline  bool GetIsStarted() { return m_bStarted; }

  inline  int SetIsCanceled(bool finished) { 
    return SetProperty((void**)&m_bCanceled, (void*)finished); }
  inline  bool GetIsCanceled() { return m_bCanceled; }

  inline  THREAD_ID_T GetThreadId() 
  { 
    m_mutex.Lock();
    THREAD_ID_T tid  = m_tid; 
    m_mutex.Unlock();
    return tid;
  }
  inline  int SetThreadId(THREAD_ID_T tid) { 
    return SetProperty((void**)&m_tid, (void*)tid);
  }

  inline double GetProgressInfoMax() { return m_progressInfo.Max; }
  inline double GetProgressInfoCurrent() { return m_progressInfo.Current; }
  inline int GetProgressInfoMsgId() { return m_progressInfo.MsgId; }

  inline int SetStartTime() { return SetProperty((void**)&m_startTime, (void*)get_time()); }
  inline int SetEndTime() { return SetProperty((void**)&m_endTime, (void*)get_time()); }
  long GetElapsedTimeMilliseconds();

  struct SProgressInfo
  {
  public:
  SProgressInfo()
    : Max(0), Current(0), MsgId(0) {}

    SProgressInfo(double max, double current, int msgid)
      : Max(max), Current(current), MsgId(msgid) {}

    double Max, Current;
    int MsgId;
  };

  virtual void CleanupForTermination();
  /* Implementors should use tid to access the thread id,
   * rather than GetThreadId() [ which should be used by
   * other threads]. 
   */
  virtual int Go(THREAD_ID_T tid) = 0;
  
  int Start();
  int Stop();
  void UpdateProgressInfo(double max, double current, int msgid);
  int UnlockAll();
  int LockAll();

 protected:
  virtual int SetProperty(void** pProperty, void* value);
  virtual int GetProperty(void* pProperty, void** value);

 private:
  CMutex m_mutex;
  void* m_pArg;
  void* m_pReturnData;
  int m_returnDataSize;
  int m_returnStatus;
  bool m_bFinished;
  bool m_bCanceled;
  THREAD_ID_T m_tid;
  SProgressInfo m_progressInfo;
  void* m_pData;
  CLOCK_T m_startTime;
  CLOCK_T m_endTime;
  bool m_bStarted;
  string m_sErrMsg;

#ifdef WIN32
 private:
  HANDLE m_handle;
 public:
  inline int SetHandle(HANDLE h) { return SetProperty((void**)&m_handle, (void*)h); }
  inline HANDLE GetHandle() { return m_handle; }
#else
#endif

};

class CProgressThreadManager
{
 public:
  // Global tracker for all threads.
  static CProgressThread* GetProgressThread(THREAD_ID_T tid) { return Instance.int_GetProgressThread(tid); }
  static bool SetProgressThread(THREAD_ID_T tid, CProgressThread* progThread) 
  { return Instance.int_SetProgressThread(tid, progThread); }
  static void Erase(THREAD_ID_T tid) { Instance.int_Erase(tid); }
  static void Erase(CProgressThread* progThread) { if(NULL != progThread) Instance.int_Erase(progThread->GetThreadId()); }

 private:
  static CProgressThreadManager Instance;
  CProgressThreadManager() {}
  CProgressThread* int_GetProgressThread(THREAD_ID_T tid);
  bool int_SetProgressThread(THREAD_ID_T tid, CProgressThread* progThread);
  void int_Erase(THREAD_ID_T tid);

 public:
  ~CProgressThreadManager();

 private:
  CMutex m_mutex;
  typedef map<THREAD_ID_T, CProgressThread*> ProgressThreadMap;
  ProgressThreadMap m_threads;
};


#endif
