/*==========================================================================;
 *
 *  Copyright (C) 1998 Microsoft Corporation.  All Rights Reserved.
 *
 *  File:       stateset.hpp
 *  Content:    State sets handling interfaces
 *
 ***************************************************************************/
#ifndef _STATESET_HPP_
#define _STATESET_HPP_

struct  CHandle;
const   DWORD __INVALIDHANDLE = 0xFFFFFFFF;

extern void InsertStateSetOp(LPDIRECT3DDEVICEI pDevI, DWORD dwOperation, DWORD dwParam, D3DSTATEBLOCKTYPE sbt);
//---------------------------------------------------------------------
// This class is used to generate sequential integer numbers (handles).
// When a handle is released it will be reused.
// Number of handles is unlimited
//
class CHandleFactory
{
public: 
    CHandleFactory() {m_dwArraySize = 0; m_Handles = NULL;}
    ~CHandleFactory();
    HRESULT Init(DWORD dwInitialSize, DWORD dwGrowSize);
    DWORD CreateNewHandle();
    void ReleaseHandle(DWORD handle);
protected:
    CHandle* CreateHandleArray(DWORD dwSize);

    CHandle        *m_Handles;      // Array of objects
    DWORD           m_dwGrowSize;   
    DWORD           m_dwArraySize;  // Number of objects in the array
    DWORD           m_Free;         // Header for free elements in the array
};
 //---------------------------------------------------------------------
// This class provides interface to the growing state set buffer
//
class CStateSetBuffer
{
public:
    CStateSetBuffer()  
        {
            m_pBuffer = NULL; 
            m_dwCurrentSize = 0; 
            m_dwBufferSize = 0;
            m_pDP2CurrCommand = 0;
        }
    CStateSetBuffer(CStateSetBuffer& src)  
        {
            m_dwCurrentSize = src.m_dwCurrentSize;
            m_dwBufferSize = src.m_dwCurrentSize;
            if(m_dwCurrentSize != 0)
            {
                m_pBuffer = new BYTE[m_dwCurrentSize];
                if(m_pBuffer == 0)
                {
                    m_dwCurrentSize = 0;
                    m_pDP2CurrCommand = 0;
                    throw DDERR_OUTOFMEMORY;
                }
                memcpy(m_pBuffer, src.m_pBuffer, m_dwCurrentSize);
            }
            else
            {
                m_pBuffer = 0;
            }
            m_pDP2CurrCommand = 0;
        }
    ~CStateSetBuffer() 
        {
            delete [] m_pBuffer;
        }
    void operator=(CStateSetBuffer& src)  
        {
            m_dwCurrentSize = src.m_dwCurrentSize; 
            if(m_dwBufferSize != m_dwCurrentSize)
            {
                m_dwBufferSize = m_dwCurrentSize;
                delete [] m_pBuffer;
                if(m_dwCurrentSize != 0)
                {
                    m_pBuffer = new BYTE[m_dwCurrentSize];
                    if(m_pBuffer == 0)
                    {
                        m_dwCurrentSize = 0;
                        m_pDP2CurrCommand = 0;
                        throw DDERR_OUTOFMEMORY;
                    }
                }
                else
                {
                    m_pBuffer = 0;
                    m_pDP2CurrCommand = 0;
                    return;
                }
            }
            memcpy(m_pBuffer, src.m_pBuffer, m_dwCurrentSize);
            m_pDP2CurrCommand = 0;
        }
    // Insert a command to the buffer. Grow buffer if necessary
    void InsertCommand(D3DHAL_DP2OPERATION, LPVOID pData, DWORD dwDataSize);
    // Reset current command
    void ResetCurrentCommand()
        {
            m_pDP2CurrCommand = 0;
        }
    // Set buffer to its initial state. Memory is not freed
    void Reset() 
        {
            m_dwCurrentSize = 0;
            m_pDP2CurrCommand = 0;
        }

    DWORD   m_dwCurrentSize;
    DWORD   m_dwBufferSize;
    BYTE    *m_pBuffer;
    //Pointer to the current position the CB1 buffer
    LPD3DHAL_DP2COMMAND m_pDP2CurrCommand;
};
//---------------------------------------------------------------------
// This class provides interface to a state set
//
const DWORD __STATESET_INITIALIZED          = 1;
// Set if we have to check if we need to restore texture stage indices
const DWORD __STATESET_NEEDCHECKREMAPPING   = 2;
class CStateSet
{
public:
    CStateSet() 
        {
            m_dwStateSetFlags = 0;
            m_dwDeviceHandle = __INVALIDHANDLE;
        }
    void InsertCommand(D3DHAL_DP2OPERATION, LPVOID pData, DWORD dwDataSize, BOOL bDriverCanHandle);
    // Mark the state set as unused. The object is not destroyed and can be
    // reused
    HRESULT Release();
    // Execute the front-end only or device state subset
    void Execute(LPDIRECT3DDEVICEI pDevI, BOOL bFrontEndBuffer);
    // Capture the current device state into the state block
    void Capture(LPDIRECT3DDEVICEI pDevI, BOOL bFrontEndBuffer);
    // Reset the current command in both buffers
    void ResetCurrentCommand()
        {
            m_FEOnlyBuffer.ResetCurrentCommand();
            m_DriverBuffer.ResetCurrentCommand();
        }
protected:
    CStateSetBuffer m_FEOnlyBuffer; // Contains commands that driver can not 
                                    // understand
    CStateSetBuffer m_DriverBuffer; // Contains commands that driver can 
                                    // understand
    DWORD   m_dwDeviceHandle;       // Some sets could not have corresponding
                                    // device buffers, so device handle is not 
                                    // equal to the user visible handle
    DWORD   m_dwStateSetFlags;
    friend class CStateSets;
};
//---------------------------------------------------------------------
// This class encapsulates handling of array of state sets
//
class CStateSets
{
public:
    CStateSets();
    ~CStateSets();
    HRESULT Init(DWORD dwFlags);
    HRESULT StartNewSet();
    void EndSet();
    // Returns current handle
    DWORD GetCurrentHandle() {return m_dwCurrentHandle;}
    // Delete state set
    void DeleteStateSet(LPDIRECT3DDEVICEI pDevI, DWORD dwHandle);
    // Returns information about state set data to be written to the device
    // Allocates a new handle for the device buffer
    void GetDeviceBufferInfo(DWORD* dwStateSetHandle, LPVOID *pBuffer, DWORD* dwBufferSize);
    // Insert a render state to the current state set
    // Throws an exception in case of error
    void InsertRenderState(D3DRENDERSTATETYPE state, DWORD dwValue, 
                           BOOL bDriverCanHandle);
    void InsertLight(DWORD dwLightIndex, LPD3DLIGHT7);
    void InsertLightEnable(DWORD dwLightIndex, BOOL bEnable);
    void InsertMaterial(LPD3DMATERIAL7);
    void InsertViewport(LPD3DVIEWPORT7);
    void InsertTransform(D3DTRANSFORMSTATETYPE state, LPD3DMATRIX lpMat);
    void InsertTextureStageState(DWORD, D3DTEXTURESTAGESTATETYPE, DWORD);
    void InsertTexture(DWORD dwStage, LPDIRECTDRAWSURFACE7 pTex);
    void InsertClipPlane(DWORD dwPlaneIndex, D3DVALUE* pPlaneEquation);
    // Execute a state set with the specified handle
    void Execute(LPDIRECT3DDEVICEI pDevI, DWORD dwHandle);
    // Capture a state set to the the specified handle
    void Capture(LPDIRECT3DDEVICEI pDevI, DWORD dwHandle);
    // Capture predefined state
    void CreatePredefined(LPDIRECT3DDEVICEI pDevI, D3DSTATEBLOCKTYPE sbt);
    void Cleanup(DWORD dwHandle);
protected:
    const DWORD m_GrowSize;
    DWORD       m_dwMaxSets;        // Maximum number of state sets
    DWORD       m_dwCurrentHandle;
    CStateSet * m_pStateSets;       // Array of state sets
    CStateSet * m_pCurrentStateSet; 
    CHandleFactory m_DeviceHandles; // Used to create device handles
    CHandleFactory m_SetHandles;    // Used to create state sets
    CStateSet   m_BufferSet;
    DWORD       m_dwFlags;
};

// This is RESERVED0 in d3dhal.h
#define D3DDP2OP_FRONTENDDATA   4           // Used by the front-end only

// This structure is used by the front-end only
typedef struct _D3DHAL_DP2FRONTENDDATA
{
    WORD   wStage;      // texture stage
    LPVOID pTexture;    // Texture pointer
} D3DHAL_DP2FRONTENDDATA, *LPD3DHAL_DP2FRONTENDDATA;

#endif //_STATESTE_HPP_