#ifndef __ENUM_HPP__
#define __ENUM_HPP__

/*==========================================================================;
 *
 *  Copyright (C) 1999-2000 Microsoft Corporation.  All Rights Reserved.
 *
 *  File:       enum.hpp
 *  Content:    Class for the enumerator object.
 *
 ***************************************************************************/

// HACK: this belongs elsewhere
#define DXGASSERT(x) DDASSERT(x)

#define HALFLAG_8BITHAL             0x00000001
#define HALFLAG_16BITHAL            0x00000004
#define HALFLAG_24BITHAL            0x00000010
#define HALFLAG_32BITHAL            0x00000040
class CBaseDevice;

// Base class for objects that own their own critical section
class CLockOwner
{
public:
    CLockOwner() :         
        m_bTakeCriticalSection(FALSE),
        m_punkSelf(NULL)
    {
        m_dwOwnerThread = ::GetCurrentThreadId();

#ifdef DEBUG
        // Prepare IsValid debug helper
        for (int i = 0; i < 8; i++)
        {
            // Set some magic numbers to check
            // object validity in debug
            m_dwDebugArray[i] = D3D8MagicNumber + i;
        }
        DXGASSERT(IsValid());
#endif // DEBUG 
    } // CLockOwner

    ~CLockOwner()
    {
        DXGASSERT(IsValid());
        DXGASSERT(m_bTakeCriticalSection == TRUE || 
                  m_bTakeCriticalSection == FALSE);
        if (m_bTakeCriticalSection)
        {
            ::DeleteCriticalSection(&m_CriticalSection);
        }
    } // ~CLockOwner

    void EnableCriticalSection()
    {
        DXGASSERT(m_bTakeCriticalSection == FALSE);
        m_bTakeCriticalSection = TRUE;
        ::InitializeCriticalSection(&m_CriticalSection);
    } // EnableCriticalSection()

#ifdef DEBUG
    BOOL IsValid() const
    {
        for (int i = 0; i < 8; i++)
        {
            if ((INT)m_dwDebugArray[i] != D3D8MagicNumber + i)
                return FALSE;
        }

        // If we are not locking then warn if we are 
        // not being called on the same thread
        if (!m_bTakeCriticalSection)
        {
            if (!CheckThread())
            {
                D3D_WARN(0, "Device that was created without D3DCREATE_MULTITHREADED "
                            "is being used by a thread other than the creation thread.");
            }
        }

        return TRUE;
    } // IsValid

#endif // DEBUG
    BOOL CheckThread() const
    {
        if (::GetCurrentThreadId() == m_dwOwnerThread)
        {
            return TRUE;
        }
        else
        {
            return FALSE;
        }
    } // CheckThread


    // Critical Section Locking
    void Lock() 
    {
        if (m_bTakeCriticalSection)
        {
            DXGASSERT(m_bTakeCriticalSection == TRUE);
            ::EnterCriticalSection(&m_CriticalSection);
        }
        else
        {
            DXGASSERT(m_bTakeCriticalSection == FALSE);
        }
        DXGASSERT(IsValid());
    }; // Lock

    void Unlock() 
    {
        DXGASSERT(IsValid());
        if (m_bTakeCriticalSection)
        {
            DXGASSERT(m_bTakeCriticalSection == TRUE);
            ::LeaveCriticalSection(&m_CriticalSection);
        }
        else
        {
            DXGASSERT(m_bTakeCriticalSection == FALSE);
        }
    }; // Unlock

    // Methods to help the API_ENTER_SUBOBJECT_RELEASE case
    ULONG AddRefOwner() 
    { 
        DXGASSERT(m_punkSelf);
        return m_punkSelf->AddRef(); 
    } // AddRefOwner
    ULONG ReleaseOwner()
    { 
        DXGASSERT(m_punkSelf);
        return m_punkSelf->Release(); 
    } // ReleaseOwner

    void SetOwner(IUnknown *punkSelf)
    {
        DXGASSERT(punkSelf);
        m_punkSelf = punkSelf;
    } // SetOwner

private:
    CRITICAL_SECTION m_CriticalSection;
    BOOL             m_bTakeCriticalSection;
    DWORD            m_dwOwnerThread;
    IUnknown        *m_punkSelf;

#ifdef DEBUG
    // Set some magic numbers to check 
    // object validity in debug
    enum
    {
        D3D8MagicNumber  = 0xD3D8D3D8
    };
    DWORD            m_dwDebugArray[8];
#endif

}; // class CLockOwner


// Device Caps/Modes Enumeration Object
class CEnum : public CLockOwner, public IDirect3D8
{
public:
    /*** IUnknown methods ***/
    STDMETHOD(QueryInterface)(REFIID riid, LPVOID * ppvObj);
    STDMETHOD_(ULONG,AddRef)();
    STDMETHOD_(ULONG,Release)();

    STDMETHODIMP RegisterSoftwareDevice(void * pInitFunction);
    STDMETHODIMP_(UINT) GetAdapterCount();
    STDMETHODIMP GetAdapterIdentifier(UINT Adapter,DWORD Flags,D3DADAPTER_IDENTIFIER8 *pIdentifier);
    STDMETHODIMP_(UINT) GetAdapterModeCount(UINT Adapter);
    STDMETHODIMP EnumAdapterModes(UINT iAdapter,UINT iMode,D3DDISPLAYMODE *pMode);
    STDMETHODIMP CheckDeviceType(UINT Adapter,D3DDEVTYPE CheckType, D3DFORMAT AdapterFormat, D3DFORMAT BackBufferFormat, BOOL Windowed);
    STDMETHODIMP CheckDeviceFormat(UINT Adapter,D3DDEVTYPE DeviceType,D3DFORMAT DisplayFormat,DWORD Usage,D3DRESOURCETYPE RType, D3DFORMAT CheckFormat); 
    STDMETHODIMP GetAdapterDisplayMode(UINT Adapter, D3DDISPLAYMODE* pMode);
    STDMETHODIMP CheckDeviceMultiSampleType(UINT Adapter,D3DDEVTYPE DeviceType,D3DFORMAT RenderTargetFormat,BOOL Windowed,D3DMULTISAMPLE_TYPE MultiSampleType);
    STDMETHODIMP CheckDepthStencilMatch(UINT Adapter, D3DDEVTYPE DeviceType, D3DFORMAT AdapterFormat, D3DFORMAT RenderTargetFormat, D3DFORMAT DepthStencilFormat);
    STDMETHODIMP GetDeviceCaps(UINT iAdapter, D3DDEVTYPE DeviceType, D3DCAPS8 *pCaps);
    STDMETHODIMP_(HMONITOR) GetAdapterMonitor(UINT iAdapter);

    STDMETHOD(CreateDevice)(
        UINT                    iAdapter,
        D3DDEVTYPE              DeviceType,
        HWND                    hwndFocusWindow,
        DWORD                   dwBehaviorFlags,
        D3DPRESENT_PARAMETERS  *pPresentationParams,
        IDirect3DDevice8      **ppDevice);

    // Public constructor for Direct3DCreate8 to call
    CEnum(UINT AppSdkVersion);

    D3DDISPLAYMODE* GetModeTable(UINT iAdapter) const
    {
        return m_AdapterInfo[iAdapter].pModeTable;
    }

    const DWORD GetNumModes(UINT iAdapter) const
    {
        return m_AdapterInfo[iAdapter].NumModes;
    }

    const BOOL NoDDrawSupport(UINT iAdapter) const
    {
        return m_AdapterInfo[iAdapter].bNoDDrawSupport;
    }
    void FillInCaps(D3DCAPS8              *pCaps, 
                    const D3D8_DRIVERCAPS *pDriverCaps,
                    D3DDEVTYPE             Type, 
                    UINT                   AdapterOrdinal) const;

    D3DFORMAT MapDepthStencilFormat(UINT        iAdapter,
                                    D3DDEVTYPE  Type, 
                                    D3DFORMAT   Format) const;

    D3DFORMAT GetUnknown16(UINT             iAdapter)
    {
        return m_AdapterInfo[iAdapter].Unknown16;
    }

    DDSURFACEDESC* GetHalOpList(UINT    iAdapter)
    {
        return m_AdapterInfo[iAdapter].HALCaps.pGDD8SupportedFormatOps;
    }

    DWORD GetNumHalOps(UINT iAdapter)
    {
        return m_AdapterInfo[iAdapter].HALCaps.GDD8NumSupportedFormatOps;
    }

#ifdef WINNT
    void    SetFullScreenDevice(UINT         iAdapter, 
                                CBaseDevice *pDevice);

    HWND ExclusiveOwnerWindow();
    BOOL CheckExclusiveMode(
            CBaseDevice* pDevice,
            LPBOOL pbThisDeviceOwnsExclusive, 
            BOOL bKeepMutex);
    void DoneExclusiveMode();
    void StartExclusiveMode();
#endif // WINNT

    // Gamma calibrator is owned by the enumerator
    void LoadAndCallGammaCalibrator(
        D3DGAMMARAMP *pRamp, 
        UCHAR * pDeviceName);

    void GetRefCaps(UINT    iAdapter);
    void GetSwCaps(UINT     iAdapter);

    void * GetInitFunction() const
    {
        return m_pSwInitFunction;
    }
    UINT GetAppSdkVersion() {return m_AppSdkVersion;}

private:

    HRESULT GetAdapterCaps(UINT                 iAdapter,
                           D3DDEVTYPE           Type,
                           D3D8_DRIVERCAPS**    ppCaps);

    DWORD                   m_cRef;
    UINT                    m_cAdapter;
    ADAPTERINFO             m_AdapterInfo[MAX_DX8_ADAPTERS];
    CBaseDevice             *m_pFullScreenDevice[MAX_DX8_ADAPTERS];
    D3D8_DRIVERCAPS         m_REFCaps[MAX_DX8_ADAPTERS];
    D3D8_DRIVERCAPS         m_SwCaps[MAX_DX8_ADAPTERS];
    VOID*                   m_pSwInitFunction;
    BOOL                    m_bDisableHAL;
    BOOL                    m_bHasExclusive;
    HINSTANCE               m_hGammaCalibrator;
    LPDDGAMMACALIBRATORPROC m_pGammaCalibratorProc;
    BOOL                    m_bAttemptedGammaCalibrator;
    BOOL                    m_bGammaCalibratorExists;
    UCHAR                   m_szGammaCalibrator[MAX_PATH];
    UINT                    m_AppSdkVersion;

}; // class CEnum

#endif // __ENUM_HPP__