///////////////////////////////////////////////////////////////////////////////
//
//         File: tmutils.h
//
//  Description: Useful Tepmlate Code, debugging stuff, general utilities
//               used throughout the termmgr and MST
//
///////////////////////////////////////////////////////////////////////////////


//
//  Note:
//
//  use tm.h to define symbols shared throughout modules composing terminal 
//  manager
//


#ifndef ___TM_UTILS_INCLUDED___
    #define ___TM_UTILS_INCLUDED___


    #if defined(_DEBUG)
        #define new new(_NORMAL_BLOCK, __FILE__, __LINE__)
    #endif

    #define DECLARE_VQI() \
        STDMETHOD(QueryInterface)(REFIID iid, void ** ppvObject) = 0; \
        STDMETHOD_(ULONG, AddRef)() = 0; \
        STDMETHOD_(ULONG, Release)() = 0;

    bool IsSameObject(IUnknown *pUnk1, IUnknown *pUnk2);

    STDAPI_(void) TStringFromGUID(const GUID* pguid, LPTSTR pszBuf);

    #ifdef UNICODE
    #define WStringFromGUID TStringFromGUID
    #else
    STDAPI_(void) WStringFromGUID(const GUID* pguid, LPWSTR pszBuf);
    #endif

    void InitMediaType(AM_MEDIA_TYPE *pmt);

    // We use ATL for our lists
    // #include <atlapp.h>
    // #define CList CSimpleArray

    // We use our own assertions
    #define ASSERT(x) TM_ASSERT(x)

    // inline fns, macros

    inline BOOL 
    HRESULT_FAILURE(
        IN HRESULT HResult
        )
    { 
        // ZoltanS: We now consider S_FALSE to be success. Hopefully nothing
        // depends on this...

        // return (FAILED(HResult) || (S_FALSE == HResult));
        return FAILED(HResult);
    }

    // ZoltanS: We now consider S_FALSE to be success. Hopefully nothing
    // depends on this...
    //    if ( FAILED(LocalHResult) || (S_FALSE == LocalHResult) )

    #define BAIL_ON_FAILURE(MacroHResult)       \
    {                                           \
        HRESULT LocalHResult = MacroHResult ;   \
        if ( FAILED(LocalHResult) )    \
        {                                                           \
            LOG((MSP_ERROR, "BAIL_ON_FAILURE - error %x", LocalHResult));   \
            return LocalHResult;                                                \
        }                                                                       \
    }


    // NULL is second - this way if an == operator
    // is defined on Ptr, the operator may be used
    #define BAIL_IF_NULL(Ptr, ReturnValue)  \
    {                                       \
        void *LocalPtr = (void *)Ptr;       \
        if ( LocalPtr == NULL )             \
        {                                   \
            LOG((MSP_ERROR, "BAIL_IF_NULL - ret value %x", ReturnValue));   \
            return ReturnValue;             \
        }                                   \
    }


    // sets the first bit to indicate error
    // sets the win32 facility code
    // this is used instead of the HRESULT_FROM_WIN32 macro because that clears the customer flag
    inline long
    HRESULT_FROM_ERROR_CODE(
        IN          long    ErrorCode
        )
    {
        // LOG((MSP_ERROR, "HRESULT_FROM_ERROR_CODE - error %x", (0x80070000 | (0xa000ffff & ErrorCode))));
        return ( 0x80070000 | (0xa000ffff & ErrorCode) );
    }

    /////////////////////////////////////////////////////////////////////////////
    /////////////////////////////////////////////////////////////////////////////
    // Better auto critical section lock
    /////////////////////////////////////////////////////////////////////////////
    /////////////////////////////////////////////////////////////////////////////

    // locks a critical section, and unlocks it automatically
    // when the lock goes out of scope ONLY if (and as many times as)
    // it holds it
    // It may also be locked and unlocked independently

    template <class T>
    class LOCAL_CRIT_LOCK 
    {
    public:

        LOCAL_CRIT_LOCK(
            IN  T *plock
            )
        {
            m_pLock = plock;
            m_pLock->Lock();
            NumLocksHeld = 1;
        }

        BOOL IsLocked(
            )
        {
            return ( (NumLocksHeld > 0)? TRUE : FALSE );
        }

        void Lock(
            )
        {
            m_pLock->Lock();
            NumLocksHeld++;
        }

        void Unlock(
            )
        {
            NumLocksHeld--;
            m_pLock->Unlock();
        }        

        ~LOCAL_CRIT_LOCK(
            ) 
        {
            while (IsLocked())
            { 
                Unlock();
            }
        }

    protected:

        DWORD   NumLocksHeld;
        T       *m_pLock;

    private:
        // make copy constructor and assignment operator inaccessible

        LOCAL_CRIT_LOCK(
            IN const LOCAL_CRIT_LOCK<T> &RefLocalLock
            );

        LOCAL_CRIT_LOCK<T> &operator=(const LOCAL_CRIT_LOCK<T> &RefLocalLock);
    };

    typedef LOCAL_CRIT_LOCK<CComObjectRoot> COM_LOCAL_CRIT_LOCK;


    #ifdef DBG

        //
        // Declare methods to log the AddRef/Release calls and values
        //
        #define DECLARE_DEBUG_ADDREF_RELEASE(x) \
            void LogDebugAddRef(DWORD dw) \
            { LOG((MSP_TRACE, "%s::AddRef() = %d", _T(#x), dw)); } \
            void LogDebugRelease(DWORD dw) \
            { LOG((MSP_TRACE, "%s::Release() = %d", _T(#x), dw)); }

        //
        // Create a template class derived from CComObject to supply
        //  the debug logic.
        //
        template <class base>
        class CTMComObject : public CComObject<base>
        {
            typedef CComObject<base> _BaseClass;
            STDMETHOD_(ULONG, AddRef)()
            {
                DWORD dwR = _BaseClass::AddRef();
                base::LogDebugAddRef(m_dwRef);
                return dwR;
            }
            STDMETHOD_(ULONG, Release)()
            {
                DWORD dwRef = m_dwRef;
                DWORD dwR = _BaseClass::Release();
                LogDebugRelease(--dwRef);
                return dwR;
            }
        };

    #else // #ifdef DBG

        #define DECLARE_DEBUG_ADDREF_RELEASE(x)

    #endif // #ifdef DBG

    // ??? why???
    #ifndef __WXUTIL__

        // locks a critical section, and unlocks it automatically
        // when the lock goes out of scope
        class CAutoObjectLock {

            // make copy constructor and assignment operator inaccessible

            CAutoObjectLock(const CAutoObjectLock &refAutoLock);
            CAutoObjectLock &operator=(const CAutoObjectLock &refAutoLock);

        protected:
            CComObjectRoot * m_pObject;

        public:
            CAutoObjectLock(CComObjectRoot * pobject)
            {
                m_pObject = pobject;
                m_pObject->Lock();
            };

            ~CAutoObjectLock() {
                m_pObject->Unlock();
            };
        };

        #define AUTO_CRIT_LOCK CAutoObjectLock lck(this);

        #ifdef _DEBUG
            #define EXECUTE_ASSERT(_x_) TM_ASSERT(_x_)
        #else
            #define EXECUTE_ASSERT(_x_) _x_
        #endif

    // ??? why???
    #endif // #ifndef __WXUTIL__


#endif // ___TM_UTILS_INCLUDED___

// eof