#ifndef __VOLUME_HPP__
#define __VOLUME_HPP__

/*==========================================================================;
 *
 *  Copyright (C) 1999-2000 Microsoft Corporation.  All Rights Reserved.
 *
 *  File:       volume.hpp
 *  Content:    Class header the volume class. This class acts
 *              as a level for the MipVolume class. The base class
 *              assumes a system-memory allocation; while the
 *              Driver sub-class will call the driver for every
 *              lock and unlock operation.
 *
 *
 ***************************************************************************/

// Includes
#include "mipvol.hpp"

//
// Each Volume implements the IDirect3DVolume8 interface. 
// To reduce overhead per level, we have
// put most of the "real" guts of each volume into the MipVolume container 
// class; i.e. most of the methods of the Volume really just end 
// up calling something in the MipVolume object.
//
// The base class implementation assumes a sys-mem allocation.
//


//
// The CVolume class is a special class that
// works solely with the CMipVolume class. Each Volume
// corresponds to a single level of the mip-volume. They are
// not stand-alone COM objects because they share the
// same life-time as their CMipVolume parent.
//
// The CDriverVolume class is declared later in this file
//

class CVolume : public IDirect3DVolume8
{
public:
    // Constructor
    CVolume(CMipVolume *pParent, 
            BYTE        iLevel,
            HANDLE      hKernelHandle
            ) :
        m_pParent(pParent),
        m_isLocked(FALSE),
        m_iLevel(iLevel),
        m_hKernelHandle(hKernelHandle)
    {
        DXGASSERT(pParent);
        DXGASSERT(hKernelHandle || (pParent->GetUserPool() == D3DPOOL_SCRATCH) );
    #ifdef DEBUG
        m_cRefDebug = 0; 
    #endif // DEBUG

        if (m_pParent->Desc()->Usage & 
                (D3DUSAGE_LOCK | D3DUSAGE_LOADONCE))
        {
            m_isLockable = TRUE;
        }
        else
        {   
            m_isLockable = FALSE;
        }

        return;
    } // CVolume

    ~CVolume()
    {
        DXGASSERT(m_cRefDebug == 0); 
        if (m_pParent->GetUserPool() != D3DPOOL_SCRATCH)
        {
            // Tell the thunk layer that we need to
            // be freed.
            DXGASSERT(m_hKernelHandle);

            D3D8_DESTROYSURFACEDATA DestroySurfData;
            DestroySurfData.hDD = m_pParent->Device()->GetHandle();
            DestroySurfData.hSurface = m_hKernelHandle;
            m_pParent->Device()->GetHalCallbacks()->DestroySurface(&DestroySurfData);
        }
#ifdef DEBUG
        else
        {
            DXGASSERT(m_pParent->GetUserPool() == D3DPOOL_SCRATCH);
        }
#endif //DEBUG
    }; // ~CVolume

public:
    // IUnknown methods
    STDMETHOD(QueryInterface) (REFIID   riid, 
                               VOID   **ppvObj);
    STDMETHOD_(ULONG,AddRef) ();
    STDMETHOD_(ULONG,Release) ();

    // IBuffer methods
    STDMETHOD(SetPrivateData)(REFGUID       riid, 
                              CONST VOID   *pvData, 
                              DWORD         cbData, 
                              DWORD         dwFlags);

    STDMETHOD(GetPrivateData)(REFGUID   riid, 
                              VOID     *pvData, 
                              DWORD    *pcbData);

    STDMETHOD(FreePrivateData)(REFGUID  riid);

    STDMETHOD(GetContainer)(REFIID riid, 
                            void **ppContainer);

    STDMETHOD(GetDevice)(IDirect3DDevice8 **ppDevice);

    // IDirect3DVolume8 methods
    STDMETHOD(GetDesc)(D3DVOLUME_DESC *pDesc);

    STDMETHOD(LockBox)(D3DLOCKED_BOX  *pLockedBox, 
                       CONST D3DBOX   *pBox, 
                       DWORD           dwFlags);
    STDMETHOD(UnlockBox)(void);

    virtual HRESULT InternalLockBox(D3DLOCKED_BOX  *pLockedBox, 
                                    CONST D3DBOX   *pBox, 
                                    DWORD           dwFlags);
    virtual HRESULT InternalUnlockBox();

    BOOL IsLocked() const
    {
        return m_isLocked;
    } // IsLocked

protected:
    CMipVolume *m_pParent;
    BOOL        m_isLocked;
    BOOL        m_isLockable;
    BYTE        m_iLevel;

    // We'll need internal handles so that
    // we can communicate call Destroy 
    // and so that CDriverVolume can call
    // Lock/Unlock etc.
    HANDLE      m_hKernelHandle;

    CBaseDevice * Device() const
    {
        return m_pParent->Device();
    } // Device

    // Debugging trick to help spew better
    // information if someone over-releases a volume
    // (Since our ref's carry over to the parent object; it
    // means that over-releases can be hard to find.)
#ifdef DEBUG
    DWORD   m_cRefDebug;
#endif // DEBUG

}; // CVolume

// The CDriverVolume is a modification of the base volume
// class. It overrides lock and unlock and routes the call to the
// driver
class CDriverVolume : public CVolume
{
public:
    // Constructor
    CDriverVolume(CMipVolume *pParent, 
                  BYTE        iLevel,
                  HANDLE      hKernelHandle
                      ) :
        CVolume(pParent, iLevel, hKernelHandle)
    {
    } // CDriverVolume

public:

    STDMETHOD(LockBox)(D3DLOCKED_BOX  *pLockedBox, 
                       CONST D3DBOX   *pBox, 
                       DWORD            dwFlags);
    STDMETHOD(UnlockBox)();

    virtual HRESULT InternalLockBox(D3DLOCKED_BOX  *pLockedBox, 
                                    CONST D3DBOX   *pBox, 
                                    DWORD           dwFlags);
    virtual HRESULT InternalUnlockBox();

}; // CDriverVolume


#endif // __VOLUME_HPP__