/*****************************************************************************
 *
 *  (C) COPYRIGHT MICROSOFT CORPORATION, 2000
 *
 *  TITLE:       w32utils.h
 *
 *  VERSION:     1.0
 *
 *  AUTHOR:      LazarI
 *
 *  DATE:        23-Dec-2000
 *
 *  DESCRIPTION: Win32 templates & utilities
 *
 *****************************************************************************/

#ifndef _W32UTILS_H
#define _W32UTILS_H

// the generic smart pointers & handles
#include "gensph.h"

////////////////////////////////////////////////
//
// class CSimpleWndSubclass
//
// class implementing simple window subclassing
// (Windows specific classes)
//
typedef LRESULT type_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
template <class inheritorClass>
class CSimpleWndSubclass
{
    WNDPROC m_wndDefProc;
    static LRESULT CALLBACK _ThunkWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
public:
    CSimpleWndSubclass(): m_hwnd(NULL), m_wndDefProc(NULL) { }
    CSimpleWndSubclass(HWND hwnd): m_hwnd(NULL), m_wndDefProc(NULL) { Attach(hwnd); }
    ~CSimpleWndSubclass() { Detach(); }

    // attach/detach
    BOOL IsAttached() const;
    BOOL Attach(HWND hwnd);
    BOOL Detach();

    // default subclass proc
    LRESULT WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

    // default proc(s)
    LRESULT DefWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
    LRESULT DefDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

    HWND m_hwnd;
};

////////////////////////////////////////////////
//
// class COleComInitializer
//
// smart OLE2, COM initializer - just declare 
// an instance wherever need to use COM, OLE2
//
class COleComInitializer
{
public:
    COleComInitializer(BOOL bOleInit = FALSE);
    ~COleComInitializer();
    operator BOOL () const;

private:
    HRESULT m_hr;
    BOOL m_bOleInit;
};

////////////////////////////////////////////////
//
// class CDllLoader
//
// smart DLL loader - calls LoadLibrary
// FreeLibrary for you.
//
class CDllLoader
{
public:
    CDllLoader(LPCTSTR pszDllName);
    ~CDllLoader();
    operator BOOL () const;
    FARPROC GetProcAddress( LPCSTR lpProcName );
    FARPROC GetProcAddress( WORD wProcOrd );

private:
    HMODULE m_hLib;
};

////////////////////////////////////////////////
// class CCookiesHolder
//
// this a utility class which allows us to pass more 
// than one pointer through a single cookie (pointer).
//
class CCookiesHolder
{
public:
    // construction/destruction
    CCookiesHolder();
    CCookiesHolder(UINT nCount);
    ~CCookiesHolder();

    // sets the count
    BOOL SetCount(UINT uCount);

    // returns the number of cookies here
    UINT GetCount() const
    { return m_uCount; } 

    // returns the cookie at this position
    template <class pType>
    pType GetCookie(UINT iIndex) const
    { 
        ASSERT(iIndex < m_uCount);
        return reinterpret_cast<pType>(m_pCookies[iIndex]);
    }

    // returns the previous cookie at this position
    template <class pType>
    pType SetCookie(UINT iIndex, pType pCookie)
    { 
        ASSERT(iIndex < m_uCount);
        pType pReturn = reinterpret_cast<pType>(m_pCookies[iIndex]);
        m_pCookies[iIndex] = reinterpret_cast<LPVOID>(pCookie);
        return pReturn;
    }

    // const & non-const operators [] 
    LPVOID  operator [] (UINT iIndex) const
    {
        ASSERT(iIndex < m_uCount);
        return m_pCookies[iIndex];
    }
    LPVOID& operator [] (UINT iIndex)
    {
        ASSERT(iIndex < m_uCount);
        return m_pCookies[iIndex];
    }

private:
    UINT m_uCount;
    LPVOID *m_pCookies;
};

////////////////////////////////////////////////
//
// template class CScopeLocker<TLOCK>
//
template <class TLOCK>
class CScopeLocker
{
public:
    CScopeLocker(TLOCK &lock): 
        m_Lock(lock), m_bLocked(false) 
    { m_bLocked = (m_Lock && m_Lock.Lock()); }

    ~CScopeLocker() 
    { if (m_bLocked) m_Lock.Unlock(); }

    operator bool () const 
    { return m_bLocked; }

private:
    bool m_bLocked;
    TLOCK &m_Lock;
};

////////////////////////////////////////////////
//
// class CCSLock - win32 critical section lock.
//
class CCSLock
{
public:
    // CCSLock::Locker should be used as locker class.
    typedef CScopeLocker<CCSLock> Locker;
   
    CCSLock(): m_bInitialized(false)
    { 
        for (;;)
        {
            __try 
            { 
                // InitializeCriticalSection may rise STATUS_NO_MEMORY exception 
                // in low memory conditions (according the SDK)
                InitializeCriticalSection(&m_CS); 
                m_bInitialized = true; 
                return;
            } 
            __except(EXCEPTION_EXECUTE_HANDLER) {}
            Sleep(100);
        }
    }

    ~CCSLock()    
    { 
        if (m_bInitialized) 
        {
            // delete the critical section only if initialized successfully
            DeleteCriticalSection(&m_CS); 
        }
    }

    operator bool () const
    { 
        return m_bInitialized; 
    }

    bool Lock()
    { 
        for (;;)
        {
            __try 
            { 
                // EnterCriticalSection may rise STATUS_NO_MEMORY exception 
                // in low memory conditions (this may happen if there is contention
                // and ntdll can't allocate the wait semaphore)
                EnterCriticalSection(&m_CS); 
                return true; 
            } 
            __except(EXCEPTION_EXECUTE_HANDLER) {}
            Sleep(100);
        }

        // we should never end up here either way
        return false;
    }

    void Unlock() 
    {
        // Unlock() should be called *ONLY* if the corresponding 
        // Lock() call has succeeded.
        LeaveCriticalSection(&m_CS); 
    }

#if DBG
    // debug code...
    bool bInside()  const
    { 
        return (m_bInitialized && m_CS.OwningThread == DWORD2PTR(GetCurrentThreadId(), HANDLE)); 
    }
    bool bOutside() const 
    { 
        return (m_bInitialized && m_CS.OwningThread != DWORD2PTR(GetCurrentThreadId(), HANDLE)); 
    }
#endif

private:
    bool m_bInitialized;
    CRITICAL_SECTION m_CS;
};

////////////////////////////////////////////////
//
// class CSemaphoreLock -  simple semaphore lock.
//
class CSemaphoreLock
{
public:
    typedef CScopeLocker<CSemaphoreLock> Locker;

    CSemaphoreLock()  { }
    ~CSemaphoreLock() { }

    void Lock()   { ASSERT(m_shSemaphore); WaitForSingleObject(m_shSemaphore, INFINITE); }
    void Unlock() { ASSERT(m_shSemaphore); ReleaseSemaphore(m_shSemaphore, 1, NULL); }

    HRESULT Create(
        LONG lInitialCount,                                     // initial count
        LONG lMaximumCount,                                     // maximum count
        LPCTSTR lpName = NULL,                                  // object name
        LPSECURITY_ATTRIBUTES lpSemaphoreAttributes = NULL      // SD
        )
    {
        m_shSemaphore = CreateSemaphore(lpSemaphoreAttributes, lInitialCount, lMaximumCount, lpName);
        return m_shSemaphore ? S_OK : E_OUTOFMEMORY;
    }

private:
    CAutoHandleNT m_shSemaphore;
};

// include the implementation of the template classes here
#include "w32utils.inl"

#endif // endif _W32UTILS_H