/*******************************************************************************
* DXSurfB.h *
*----------*
*   Description:
*       This is the header file for the CDXBaseSurface implementation. It is
*   used as a base class to implement read-only procedural DXSurfaces.
*-------------------------------------------------------------------------------
*  Created By: RAL                                  Date: 02/12/1998
*  Copyright (C) 1998 Microsoft Corporation
*  All Rights Reserved
*
*-------------------------------------------------------------------------------
*  Revisions:
*
*******************************************************************************/

#ifndef __DXSurfB_H__
#define __DXSurfB_H__

#include "dtbase.h"

class CDXBaseSurface;
class CDXBaseARGBPtr;

class ATL_NO_VTABLE CDXBaseSurface :
    public CDXBaseNTo1, 
    public IDXSurface,
    public IDXSurfaceInit
{
    /*=== ATL Setup ===*/
    public:
        BEGIN_COM_MAP(CDXBaseSurface)
        COM_INTERFACE_ENTRY(IDXSurface)
        COM_INTERFACE_ENTRY(IDXSurfaceInit)
        COM_INTERFACE_ENTRY_CHAIN(CDXBaseNTo1)
        END_COM_MAP()

    DECLARE_GET_CONTROLLING_UNKNOWN()

    /*=== Member Data ===*/
    public:
        ULONG           m_Height;
        ULONG           m_Width;
        DWORD           m_dwStatusFlags;
        HANDLE          m_hSemaphore;
        ULONG           m_ulLocks;
        ULONG           m_ulThreadsWaiting;
        CDXBaseARGBPtr  *m_pFreePtr;
        DWORD           m_dwAppData;
        CComAutoCriticalSection m_MPWorkProcCrit;   // See comments in LockSurface for details

        CDXBaseSurface();
        HRESULT FinalConstruct();
        void FinalRelease();

        //
        //  IDXBaseObject
        //
        STDMETHODIMP GetGenerationId(ULONG *pGenId);
        STDMETHODIMP IncrementGenerationId(BOOL bRefresh);
        STDMETHODIMP GetObjectSize(ULONG *pulze);

        //
        //  Overridden methods of DXTransform
        //
        STDMETHODIMP MapBoundsIn2Out(const DXBNDS *pInBounds, ULONG ulNumInBnds,
                                     ULONG /*ulOutIndex*/, DXBNDS *pOutBounds );

        //
        //  IDXSurfaceInit
        //
        STDMETHODIMP InitSurface(IUnknown *pDirectDraw,
                                 const DDSURFACEDESC * pDDSurfaceDesc,
                                 const GUID * pFormatId,
                                 const DXBNDS *pBounds,
                                 DWORD dwFlags);
        //
        //  IDXSurface methods
        //
        STDMETHODIMP GetPixelFormat(GUID *pFormat, DXSAMPLEFORMATENUM *pSampleEnum);
        STDMETHODIMP GetBounds(DXBNDS *pBounds);
        STDMETHODIMP GetStatusFlags(DWORD * pdwStatusFlags);
        STDMETHODIMP SetStatusFlags(DWORD dwStatusFlags);
        STDMETHODIMP GetDirectDrawSurface(REFIID riid, void **ppSurface);
        STDMETHODIMP LockSurface(const DXBNDS *pBounds, ULONG ulTimeOut, DWORD dwFlags,
                                 REFIID riid, void **ppPointer, DWORD * pGenerationId);
        STDMETHODIMP SetAppData(DWORD dwAppData)
        {
            m_dwAppData = dwAppData;
            return S_OK;
        }
        STDMETHODIMP GetAppData(DWORD *pdwAppData)
        {
            if (DXIsBadWritePtr(pdwAppData, sizeof(*pdwAppData)))
            {
                return E_POINTER;
            }
            *pdwAppData = m_dwAppData;
            return S_OK;
        }


        //
        //  These methods aren't supported by procedural surfaces...
        //
        STDMETHODIMP GetColorKey(DXSAMPLE *pColorKey)
        {
            return E_NOTIMPL;
        }

        STDMETHODIMP SetColorKey(DXSAMPLE pColorKey)
        {
            return E_NOTIMPL;
        }

        STDMETHODIMP LockSurfaceDC(const DXBNDS *pBounds, ULONG ulTimeOut, DWORD dwFlags, IDXDCLock **ppDXLock)
        {
            return E_NOTIMPL;
        }

        //
        //  Surfaces should override this.
        //
        virtual ULONG OnGetObjectSize(void) { return sizeof(*this); }

        //
        //  This work procedure can be overridden by the derived class to improve performance
        //  or execution of the transform by directly producing data in large blocks if desired.
        //
        virtual HRESULT WorkProc(const CDXTWorkInfoNTo1 & WI, BOOL* pbContinueProcessing)
        {
            return DXBitBlt(OutputSurface(), WI.OutputBnds, this, WI.DoBnds, m_dwBltFlags, m_ulLockTimeOut);
        }

        //
        //  Pick interface needs to test procedural surface.
        //
        virtual HRESULT OnSurfacePick(const CDXDBnds & OutPoint, ULONG & ulInputIndex, CDXDVec & InVec);

        //
        //  Helper functions
        //

        //  _EnterCritWith0PtrLocks()
        //
        //  This function is similar to calling Lock() except that it will wait until there
        //  are no pointers to the surface before returning.  This should be used whenever you
        //  are going to change the state of a surface, for example the size or some other
        //  property that the read pointers rely on.
        //
        //  WARNING:  You must be sure that one of the following is true:
        //      1) The objects critical section has NOT been taken prior to calling this function
        //   or 2) There are no pointers to the surface taken prior to calling this function.
        //
        //  Case 2 is useful in nested function calls.  If the outer function has already used this
        //  function to enter the critical section, then it is OK to use it on the inner nested
        //  function.  If the object's lock is taken, but there are outstanding pointers, YOU WILL DEADLOCK!
        //
        inline void _EnterCritWith0PtrLocks(void)
        {
            while (TRUE)
            {
                Lock();
                if (m_ulLocks == 0) break;
                m_ulThreadsWaiting++;
                Unlock();
                WaitForSingleObject(m_hSemaphore, INFINITE);
            }
        }
        //
        //  Virtual functions derived class MUST override
        //
        virtual const GUID & SurfaceCLSID() = 0;
        virtual HRESULT CreateARGBPointer(CDXBaseSurface * pSurface, CDXBaseARGBPtr ** ppPtr) = 0;
        virtual void DeleteARGBPointer(CDXBaseARGBPtr *pPtr) = 0;
    
        //
        //  Class may override this virtual function to return a more accurate enum
        //  for example, no transparency or translucency.
        //
        virtual DXSAMPLEFORMATENUM SampleFormatEnum()
        {
            return (DXSAMPLEFORMATENUM)(DXPF_NONSTANDARD | DXPF_TRANSPARENCY | DXPF_TRANSLUCENCY);
        }

        //
        //  Class may override this virtual function to perform necessary computations
        //  when the size of the surface changes.  The base class will only call this
        //  function from InitSurface.  You may choose to call it from other interfaces
        //  you implement, for example IDXTScaleOutput.
        //
        //  This function will be called with the critical section taken and 0 outstanding
        //  surface pointers (_EnterCritWith0PtrLocks).
        //  
        virtual HRESULT OnSetSize(ULONG Width, ULONG Height)
        {
            if (m_Width != Width || m_Height != Height)
            {
                m_Width = Width;
                m_Height = Height;
                m_dwGenerationId++;
            }
            return S_OK;
        }

        //
        //  Internal functions for base class
        //
        void _InternalUnlock(CDXBaseARGBPtr *pPtrToUnlock);

        //
        //  Static member function for registering surface
        //
        static HRESULT RegisterSurface(REFCLSID rcid, int ResourceId, ULONG cCatImpl, const CATID * pCatImpl,
                                       ULONG cCatReq, const CATID * pCatReq, BOOL bRegister);
};

struct DXPtrFillInfo
{
    DXBASESAMPLE *  pSamples;
    ULONG           cSamples;
    ULONG           x;
    ULONG           y;
    BOOL            bPremult;
};


class CDXBaseARGBPtr : public IDXARGBReadPtr
{
public:
    CDXBaseARGBPtr    * m_pNext;
    CDXBaseSurface    * m_pSurface;
    ULONG               m_ulRefCount;
    DXPtrFillInfo       m_FillInfo;
    RECT                m_LockedRect;
    DXRUNINFO           m_RunInfo;
    
    CDXBaseARGBPtr(CDXBaseSurface *pSurface) :
        m_pSurface(pSurface),
        m_pNext(NULL),
        m_ulRefCount(0) {}

    //
    //  IUnknown
    //
    STDMETHODIMP QueryInterface(REFIID riid, void ** ppv);
    ULONG STDMETHODCALLTYPE AddRef();
    ULONG STDMETHODCALLTYPE Release();
    //
    //  IDXARGBReadPtr
    //
    HRESULT STDMETHODCALLTYPE GetSurface(REFIID riid, void **ppSurface);
    DXSAMPLEFORMATENUM STDMETHODCALLTYPE GetNativeType(DXNATIVETYPEINFO *pInfo);
    void STDMETHODCALLTYPE Move(long cSamples);
    void STDMETHODCALLTYPE MoveToRow(ULONG y);
    void STDMETHODCALLTYPE MoveToXY(ULONG x, ULONG y);
    ULONG STDMETHODCALLTYPE MoveAndGetRunInfo(ULONG Row, const DXRUNINFO ** ppInfo);
    DXSAMPLE *STDMETHODCALLTYPE Unpack(DXSAMPLE *pSamples, ULONG cSamples, BOOL bMove);
    DXPMSAMPLE *STDMETHODCALLTYPE UnpackPremult(DXPMSAMPLE *pSamples, ULONG cSamples, BOOL bMove);
    void STDMETHODCALLTYPE UnpackRect(const DXPACKEDRECTDESC *pDesc);

    //
    //  Virtual function derived class MUST override
    //
    virtual void FillSamples(const DXPtrFillInfo & FillInfo) = 0;

    //
    //  Virtual functions derived class MAY want to override (but you will need to call the base class too)
    //
    virtual HRESULT InitFromLock(const RECT & rect, ULONG ulTimeOut, DWORD dwLockFlags, REFIID riid, void ** ppv);
};

//=== Macro Definitions ============================================

#define DECLARE_REGISTER_DX_SURFACE(id)\
    static HRESULT WINAPI UpdateRegistry(BOOL bRegister) \
        { \
            CATID cat[2]; \
            cat[0] = CATID_DXSurface; \
            cat[1] = CATID_DXImageTransform; \
            return RegisterSurface(GetObjectCLSID(), (id), 2, cat, 0, NULL, bRegister); \
        } 

#define DECLARE_REGISTER_DX_AUTHORING_SURFACE(id)\
    static HRESULT WINAPI UpdateRegistry(BOOL bRegister) \
        { \
            CATID cat[3]; \
            cat[0] = CATID_DXSurface; \
            cat[1] = CATID_DXImageTransform; \
            cat[2] = CATID_DXAuthoringTransform; \
            return RegisterSurface(GetObjectCLSID(), (id), 3, cat, 0, NULL, bRegister); \
        } 

#endif