#if !defined UTILITY
#define UTILITY

#include <math.h>
#include <Afxmt.h>
#include <afxtempl.h>
#include <memory>
#include <stdexcept>

#define CAST_BOOL(a) ((a) ? TRUE : FALSE)

#ifdef  BUILD_UTILITYDLL
#define     UTILITYDLL  __declspec(dllexport)
#else
#define     UTILITYDLL  __declspec(dllimport)
#endif

#if _MSC_VER > 1200 // Version Higher than VC6
/* D:\MITLIB\mcctrdll need this to prevent linker error LNK2019: unresolved external symbol
   "__declspec(dllimport) public: __thiscall ATL::CStringT<char,class StrTraitMFC<char,class ATL::ChTraitsCRT<char>>>::CStringT<char,class StrTraitMFC<char,class ATL::ChTraitsCRT<char>>>(char const *)" (__imp_??0?$CStringT@DV?$StrTraitMFC@DV?$ChTraitsCRT@D@ATL@@@@@ATL@@QAE@PBD@Z)*/
template class __declspec (dllexport) CArray<int, int>;
#endif

void UTILITYDLL DebugLog(LPSTR lpFormat, ...);
void UTILITYDLL DebugLog(bool bTraceData, LPSTR lpFormat, ...);
void UTILITYDLL DebugLog(LPCTSTR lpName, bool bTraceData, LPSTR lpFormat, ...);

void UTILITYDLL DebugLogTiming(LPSTR lpFormat, ...);
void UTILITYDLL DebugLogTiming(bool bTraceData, LPSTR lpFormat, ...);
void UTILITYDLL DebugLogTiming(LPCTSTR lpName, bool bTraceData, LPSTR lpFormat, ...);

void UTILITYDLL StartDebugLog();
void UTILITYDLL StopDebugLog();

void UTILITYDLL StartDebugTimingLog();
void UTILITYDLL StopDebugTimingLog();

bool UTILITYDLL IsDebugLogStarted();
bool UTILITYDLL IsDebugLogTimingStarted();

// All GUI Activities Events Log
void UTILITYDLL GUIEventLog(LPSTR lpFormat, ...);

void UTILITYDLL StartGUIEventLog();
void UTILITYDLL StopGUIEventLog();

class UTILITYDLL CMyWaitTimeoutException : public std::exception
{
public:
    CMyWaitTimeoutException();
    virtual ~CMyWaitTimeoutException();
};

class UTILITYDLL CMyWaitInterruptedException : public std::exception
{
public:
    CMyWaitInterruptedException();
    virtual ~CMyWaitInterruptedException();
};

class UTILITYDLL CMyWait
{
public:
    CMyWait();
    virtual ~CMyWait();
// ************************************
// Method:    Wait
// FullName:  CMyWait::Wait
// Access:    public
// Returns:   DWORD
// Qualifier:
// Parameter: HANDLE handle
// Parameter: DWORD dwTimeout
// ************************************
///////////////////////////////////////////////////////////////////////////////
// return : 0 -> timeout or error
//          1 -> first event signal
    static DWORD Wait(HANDLE handle, DWORD dwTimeout);
///////////////////////////////////////////////////////////////////////////////
// return : 1 -> first event signal
//          throw exception on timeout or error
    static DWORD WaitEx(HANDLE handle, DWORD dwTimeout);
///////////////////////////////////////////////////////////////////////////////
// return : 0 -> timeout or error
//          1 -> first event signal
//          n -> n th event signal
    static DWORD Wait(LPHANDLE handle, BOOL bWaitForAll, DWORD dwTimeout, int nNoOfHandle);
///////////////////////////////////////////////////////////////////////////////
// return : 1 -> first event signal
//          n -> n th event signal
//          throw exception on timeout or error
    static DWORD WaitEx(LPHANDLE handle, BOOL bWaitForAll, DWORD dwTimeout, int nNoOfHandle);
    static void  Interrupt(DWORD threadId);

// This sleep function will return after the delay in millis and
// continue to process messages send to the Main UI message pump
    static void Sleep(DWORD millis);
};

class UTILITYDLL CEvt : public CMyWait
{
public:
    CEvt();                       // create an object without name, this object can only be used in the local thread
    explicit CEvt(LPCTSTR pszNo); // create an object with the name pszNo

    HANDLE GetEvt(BOOL bState);
    ~CEvt();

    BOOL operator =(const BOOL bState);
    BOOL operator ==(const BOOL bState);
    BOOL operator !=(const BOOL bState);
// Operations
    BOOL WaitEvt(DWORD dwTimeOut = INFINITE);
    BOOL WaitResetEvt(DWORD dwTimeOut = INFINITE);
    BOOL WaitEvtEx(DWORD dwTimeOut = INFINITE);
    BOOL WaitResetEvtEx(DWORD dwTimeOut = INFINITE);
    BOOL Reset(void);
    BOOL Set(void);

    CString GetEvtName();

private:
    CString m_csName;
    CEvent  m_evtActivate;
    CEvent  m_evtDeactivate;
};

class UTILITYDLL CHighResPerformanceCounter
{
public:
//  default constructor
    CHighResPerformanceCounter();

//  default destructor
    virtual ~CHighResPerformanceCounter();

//  record start count
    void StartCnt(void);

//  record stop count
    void StopCnt(void);

    /// Gets current time span in msec.
    LONGLONG GetCurrentTimeSpanInMsec();
    double   GetCurrentTimeSpanInSec();
    double   GetCurrentTimeSpanInMin();
    double   GetCurrentTimeSpanInHr();

    LONGLONG GetTimeSpanInMsec();
    double   GetTimeSpanInSec();
    double   GetTimeSpanInMin();
    double   GetTimeSpanInHr();

    LONG GetTimeRemaining(LONG timeout);

//  get current counter
    static LONGLONG GetCurrentCnt();

//  cal msec time span in count
    static LONGLONG CalMsecTimeSpanInCnt(LONGLONG lMsec);

//  cal time span in msec
    static LONGLONG CalTimeSpanInMsec(LONGLONG lcount);

//  cal time span in sec
    static double CalTimeSpanInSec(LONGLONG lcount);

//  cal time span in mins
    static double CalTimeSpanInMin(LONGLONG lCnt);

//  cal time span in hours
    static double CalTimeSpanInHr(LONGLONG lCnt);

private:
//  Start count
    LARGE_INTEGER m_lStartCnt;

//  Stop count
    LARGE_INTEGER m_lStopCnt;
};

class UTILITYDLL CIniFile
{
public:
    CIniFile(LPCTSTR sFileName = nullptr, LPCTSTR sSectionName = nullptr);
    virtual ~CIniFile();

    CIniFile& SetFileName(LPCTSTR sFileName);
    CIniFile& SetSectionName(LPCTSTR m_sSectionName);

//  Get SectionName
    LPCTSTR GetSectionName();

//  Get FileName
    LPCTSTR GetFileName();

    BOOL    WriteString(LPCTSTR lpKeyName, LPCTSTR lpString);
    BOOL    WriteLong(LPCTSTR lpKeyName, long lLong);
    BOOL    WriteDouble(LPCTSTR lpKeyName, double dDouble);
    CString GetString(LPCTSTR lpKeyName, LPCTSTR lpDefaultString, BOOL bCreateIfFail = TRUE);
    long    GetLong(LPCTSTR lpKeyName, long lDefaultLong, BOOL bCreateIfFail = TRUE);
    double  GetDouble(LPCTSTR lpKeyName, double dDefaultDouble, BOOL bCreateIfFail = TRUE);
    bool    GetBool(LPCTSTR lpKeyName, LPCTSTR lpDefaultString, BOOL bCreateIfFail = TRUE);

//  write longlong value to file
    BOOL WriteLongLong(LPCTSTR lpKeyName, LONGLONG lLong);

//  get longlong value from file
    LONGLONG GetLongLong(LPCTSTR lpKeyName, LONGLONG lDefaultLong, BOOL bCreateIfFail = TRUE);

//  write longlong value to file
    static BOOL WriteLongLong(LPCTSTR pszFileName, LPCTSTR pszSectionName, LPCTSTR lpKeyName, LONGLONG lLong);

//  get long long value from file
    static LONGLONG GetLongLong(LPCTSTR pszFileName, LPCTSTR pszSectionName, LPCTSTR lpKeyName, LONGLONG lDefaultLong, BOOL bCreateIfFail = TRUE);

    static BOOL    WriteString(LPCTSTR pszFileName, LPCTSTR pszSectionName, LPCTSTR lpKeyName, LPCTSTR lpString);
    static BOOL    WriteLong(LPCTSTR pszFileName, LPCTSTR pszSectionName, LPCTSTR lpKeyName, long lLong);
    static BOOL    WriteDouble(LPCTSTR pszFileName, LPCTSTR pszSectionName, LPCTSTR lpKeyName, double dDouble);
    static CString GetString(LPCTSTR pszFileName, LPCTSTR pszSectionName, LPCTSTR lpKeyName, LPCTSTR lpDefaultString, BOOL bCreateIfFail = TRUE);
    static long    GetLong(LPCTSTR pszFileName, LPCTSTR pszSectionName, LPCTSTR lpKeyName, long lDefaultLong, BOOL bCreateIfFail = TRUE);
    static double  GetDouble(LPCTSTR pszFileName, LPCTSTR pszSectionName, LPCTSTR lpKeyName, double dDefaultDouble, BOOL bCreateIfFail = TRUE);
    static bool    GetBool(LPCTSTR pszFileName, LPCTSTR pszSectionName, LPCTSTR lpKeyName, LPCTSTR lpDefaultString, BOOL bCreateIfFail = TRUE);

// attributes
private:
    static const int MAX_INI_FILE_PATH = 512;
    char             m_sFileName[MAX_INI_FILE_PATH];
    char             m_sSectionName[MAX_INI_FILE_PATH];
};

/////////////////////////////////////////////////////////////////////////////
// CMyPoint

class UTILITYDLL CMyPoint
{
public:
    CMyPoint()
    {
        x = 0;
        y = 0;
    }

    double x;
    double y;
};

/////////////////////////////////////////////////////////////////////////////
// CThreePoint
//              x3, y3
//               _______________
//              |               |
//              |       -       |
//              |       }       |
//              |   + __|___-   |
//              |       |       |
//              |       |       |
//              |       +       |
//              |_______________|
//
//              x1, y1          x2, y2
//
//  dx = x3 - x1 / No of y pt
//  dy = y2 - y1 / No of x pt
/////////////////////////////////////////////////////////////////////////////
// tan      theta   = adjacent / opposite   = y / x
// cos      theta   = adjacent / hypo       = x / hypo
// sine     theta   = opposite / hypo       = y / hypo
// cotan    theta   = opposite / adjacent   = x / y
// secant   theta   = hypo / adjacent       = hypo / x
// cosecant theta   = hypo / opposite       = hypo / y
#define PI              3.1415926535897932384626433832795
#define RAD_TO_DEGREE   180 / PI // 1 RADIAN = 57.29577951

class UTILITYDLL CThreePoint
{
public:
    CThreePoint();

    enum
    {
        PT1,
        PT2,
        PT3,
    };

    double Getx(int iPt);

    double Gety(int iPt);

    void Setx(int iPt, double x);

    void Sety(int iPt, double y);

    void Setx(double p1, double p2, int iPitchStep = 0);

    void Setx(double p1, double p2, double p3, int iStep = 0, int iPitchStep = 0);

    void Sety(double p1, double p2, int iStep = 0);

    void Sety(double p1, double p2, double p3, int iStep = 0, int iPitchStep = 0);

    double Pitchx(void);

    double Pitchy(void);

    double Dx(void);

    double Dy(void);

    double Xcentre(void);

    double Ycentre(void);

//  get theta in radian
    double Theta();

//  get theta in degree
    double ThetaInDegree();

    double x,
           y;
protected:
    CMyPoint pt1,
             pt2,
             pt3;
    CMyPoint delta;
    CMyPoint pitch;
};

// class CInput;

class UTILITYDLL CTrigger : public CMyWait
{
public:
    CTrigger();
    virtual ~CTrigger();

    void InsertTrigger(CEvt* pEvt, BOOL bState);
    void InsertTrigger(HANDLE handle);
//  void InsertInput(CInput* pInput, BOOL bState, DWORD dwDebounce, DWORD dwTimeout);
    DWORD Wait(DWORD dwTimeOut = INFINITE);
    BOOL  WaitForAll(DWORD dwTimeOut = INFINITE);
    void  Reset();

protected:
    DWORD                             m_dwCount;
    CTypedPtrArray<CPtrArray, HANDLE> m_aHandle;
private:
    DWORD WaitFor(DWORD dwTimeOut, BOOL bWaitForAll);
};

class UTILITYDLL CSharedMemory : public CObject
{
public:
    CSharedMemory();
    virtual ~CSharedMemory();
    DWORD CreateSharedMem(LPCTSTR pszName, DWORD dwBytes);
// Will return 0 or ERROR_ALREADY_EXISTS if it already exists.
    LPVOID Open(DWORD dwTimeout = INFINITE); // SM must be opened before use. The pointer
                                             // returns must be type casted to it type
                                             // Returns NULL if the SM was not created ot failed
    BOOL Close();                            // SM should be closed after use so that others can open the SM
    BOOL    IsOpen()
    {
        return m_bOpen;
    }                                        // Check if SM is opened

    DWORD   GetLength()
    {
        return m_dwLength;
    }

#ifdef _DEBUG
    virtual void AssertValid() const;
    virtual void Dump(CDumpContext& dc) const;
#endif

protected:
    DECLARE_DYNAMIC(CSharedMemory)

private:
    HANDLE m_hMutex;
    LPVOID m_pvData;
    HANDLE m_hMapping;
    BOOL   m_bOpen;
    DWORD  m_dwLength;
};

#endif
