/*
 * COPYRIGHT:            See COPYING in the top level directory
 * PROJECT:              ReactOS DirectX
 * FILE:                 dll/directx/ddraw/Ddraw/ddraw_main.c
 * PURPOSE:              IDirectDraw7 Implementation
 * PROGRAMMER:           Magnus Olsen, Maarten Bosma
 *
 */

/* TODO
 * add warper functions for dx 1 - 6
 * map the  DirectDraw4_Vtable, DirectDraw2_Vtable, DirectDraw_Vtable
 * table to right version of the functions
 */



#include "rosdraw.h"

#include <string.h>

LPDDRAWI_DIRECTDRAW_INT
internal_directdraw_int_alloc(LPDDRAWI_DIRECTDRAW_INT This)
{
    LPDDRAWI_DIRECTDRAW_INT  newThis;
    DxHeapMemAlloc(newThis, sizeof(DDRAWI_DIRECTDRAW_INT));
    if (newThis)
    {
        newThis->lpLcl = This->lpLcl;
        newThis->lpLink = This;
    }

    return  newThis;
}

HRESULT WINAPI
Main_DirectDraw_QueryInterface (LPDDRAWI_DIRECTDRAW_INT This,
                                REFIID id,
                                LPVOID *obj)
{
    HRESULT retVal = DD_OK;

    DX_WINDBG_trace();

    _SEH2_TRY
    {
        /* FIXME
            the D3D object can be obtained from here
            Direct3D7
        */
        if (IsEqualGUID(&IID_IDirectDraw7, id))
        {
            if (This->lpVtbl != &DirectDraw7_Vtable)
            {
                This = internal_directdraw_int_alloc(This);
                if (!This)
                {
                    retVal = DDERR_OUTOFVIDEOMEMORY;
                    _SEH2_LEAVE;
                }
            }

            This->lpVtbl = &DirectDraw7_Vtable;
            *obj = This;
            Main_DirectDraw_AddRef(This);
        }
        else if (IsEqualGUID(&IID_IDirectDraw4, id))
        {
            if (This->lpVtbl != &DirectDraw4_Vtable)
            {
                This = internal_directdraw_int_alloc(This);
                if (!This)
                {
                    retVal = DDERR_OUTOFVIDEOMEMORY;
                    _SEH2_LEAVE;
                }
            }

            This->lpVtbl = &DirectDraw4_Vtable;
            *obj = This;
            Main_DirectDraw_AddRef(This);
        }

        else if (IsEqualGUID(&IID_IDirectDraw2, id))
        {
            if (This->lpVtbl != &DirectDraw2_Vtable)
            {
                This = internal_directdraw_int_alloc(This);
                if (!This)
                {
                    retVal = DDERR_OUTOFVIDEOMEMORY;
                    _SEH2_LEAVE;
                }
            }

            This->lpVtbl = &DirectDraw2_Vtable;
            *obj = This;
            Main_DirectDraw_AddRef(This);
        }
        else if (IsEqualGUID(&IID_IDirectDraw, id))
        {
            if (This->lpVtbl != &DirectDraw_Vtable)
            {
                This = internal_directdraw_int_alloc(This);
                if (!This)
                {
                    retVal = DDERR_OUTOFVIDEOMEMORY;
                    _SEH2_LEAVE;
                }
            }

            This->lpVtbl = &DirectDraw_Vtable;
            *obj = This;
            Main_DirectDraw_AddRef(This);
        }
        else
        {
            *obj = NULL;
            DX_STUB_str("E_NOINTERFACE");
            retVal = E_NOINTERFACE;
        }
    }
    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
    {
    }
    _SEH2_END;

    return retVal;
}

/*++
* @name DDraw->AddRef
* @implemented
*
* The function DDraw->AddRef manages all ref counters in the COM object DDraw->

* @return
* Returns the local Ref counter value for the COM object
*
* @remarks.
* none
*
*--*/
ULONG WINAPI
Main_DirectDraw_AddRef (LPDDRAWI_DIRECTDRAW_INT This)
{
    ULONG retValue = 0;

    DX_WINDBG_trace();

    /* Lock the thread */
    AcquireDDThreadLock();

    _SEH2_TRY
    {
        /* Increment the internal ref counter */
        This->dwIntRefCnt++;

        /* Increment the local internal ref counter */
        This->lpLcl->dwLocalRefCnt++;

        if (This->lpLcl->lpGbl != NULL)
        {
            /* Increment the global internal ref counter */
            This->lpLcl->lpGbl->dwRefCnt++;
        }
    }
    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
    {
    }
    _SEH2_END;

    _SEH2_TRY
    {
        retValue = This->dwIntRefCnt;
    }
    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
    {
        retValue = 0;
    }
    _SEH2_END;

    /* Release the thread lock */
    ReleaseDDThreadLock();

    /* Return the local Ref counter */
    return retValue;
}




ULONG WINAPI
Main_DirectDraw_Release (LPDDRAWI_DIRECTDRAW_INT This)
{
    ULONG Counter = 0;

    DX_WINDBG_trace();

    /* Lock the thread */
    AcquireDDThreadLock();

    _SEH2_TRY
    {
        if (This!=NULL)
        {
            This->lpLcl->dwLocalRefCnt--;
            This->dwIntRefCnt--;

            if (This->lpLcl->lpGbl != NULL)
            {
                This->lpLcl->lpGbl->dwRefCnt--;
            }

            if ( This->lpLcl->lpGbl->dwRefCnt == 0)
            {
                // set resolution back to the one in registry
                /*if(This->cooperative_level & DDSCL_EXCLUSIVE)
                {
                    ChangeDisplaySettings(NULL, 0);
                }*/

                Cleanup(This);
            }

            /* FIXME cleanup being not call why ?? */
            Counter = This->dwIntRefCnt;
        }
        else
        {
            Counter = This->dwIntRefCnt;
        }
    }
    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
    {
    }
    _SEH2_END;

    /* Release the thread lock */
    ReleaseDDThreadLock();

    return Counter;
}


HRESULT WINAPI
Main_DirectDraw_Initialize (LPDDRAWI_DIRECTDRAW_INT This, LPGUID lpGUID)
{
	return DDERR_ALREADYINITIALIZED;
}


/*++
* @name DDraw->Compact
* @implemented
*
* In exclusive mode the function DDraw->Compact returns DERR_NOEXCLUSIVEMODE, otherwise it returns DD_OK
*
* @return
* Returns only error code DD_OK or DERR_NOEXCLUSIVEMODE
*
* @remarks.
*  Microsoft says Compact is not implemented in ddraw.dll, but it returns  DDERR_NOEXCLUSIVEMODE or DD_OK
*
*--*/
HRESULT WINAPI
Main_DirectDraw_Compact(LPDDRAWI_DIRECTDRAW_INT This)
{
    HRESULT retVal = DD_OK;

    DX_WINDBG_trace();

    /* Lock the thread */
    AcquireDDThreadLock();

    _SEH2_TRY
    {
        /* Check if Exclusive mode has been activated */
        if (This->lpLcl->lpGbl->lpExclusiveOwner != This->lpLcl)
        {
            retVal = DDERR_NOEXCLUSIVEMODE;
        }
    }
    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
    {
    }
    _SEH2_END;

    /* Release the thread lock */
    ReleaseDDThreadLock();

    return retVal;
}

HRESULT WINAPI
Main_DirectDraw_GetAvailableVidMem(LPDDRAWI_DIRECTDRAW_INT This, LPDDSCAPS ddscaps, LPDWORD dwTotal, LPDWORD dwFree)
{
    DDSCAPS2 myddscaps;
    HRESULT retValue = DD_OK;

    ZeroMemory(&myddscaps, sizeof(DDSCAPS2));

    _SEH2_TRY
    {
        myddscaps.dwCaps =  ddscaps->dwCaps;
        retValue = Main_DirectDraw_GetAvailableVidMem4(This, &myddscaps, dwTotal, dwFree);
    }
    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
    {
         retValue = DDERR_INVALIDPARAMS;
    }
    _SEH2_END;

    return retValue;
}

HRESULT WINAPI
Main_DirectDraw_GetAvailableVidMem4(LPDDRAWI_DIRECTDRAW_INT This, LPDDSCAPS2 ddscaps,
                   LPDWORD dwTotal, LPDWORD dwFree)
{
    HRESULT retVal = DD_OK;
    DDHAL_GETAVAILDRIVERMEMORYDATA  memdata;

    DX_WINDBG_trace();

    _SEH2_TRY
    {
        // There is no HEL implementation of this api
        if (!(This->lpLcl->lpDDCB->HALDDMiscellaneous.dwFlags & DDHAL_MISCCB32_GETAVAILDRIVERMEMORY) ||
            (This->lpLcl->lpGbl->dwFlags & DDRAWI_NOHARDWARE) )
        {
            retVal = DDERR_NODIRECTDRAWHW;
        }
        else
        {
            if ((!dwTotal && !dwFree) || !ddscaps)
            {
                retVal = DDERR_INVALIDPARAMS;
                _SEH2_LEAVE;
            }

            if ( ddscaps->dwCaps & (DDSCAPS_BACKBUFFER  | DDSCAPS_COMPLEX   | DDSCAPS_FLIP |
                                    DDSCAPS_FRONTBUFFER | DDSCAPS_PALETTE   | DDSCAPS_SYSTEMMEMORY |
                                    DDSCAPS_VISIBLE     | DDSCAPS_WRITEONLY | DDSCAPS_OWNDC))
            {
                retVal = DDERR_INVALIDPARAMS;
                _SEH2_LEAVE;
            }


            /*   ddscaps->dwCaps2 & 0x01
                this flag is outdate and are
                set to 0 in ms dxsdk  the name of
                this flag is DDSCAPS2_HARDWAREDEINTERLACE
            */

            if ( ddscaps->dwCaps2 & 0x01)
            {
                retVal = DDERR_INVALIDCAPS;
                _SEH2_LEAVE;
            }

            if ( ddscaps->dwCaps3 & ~( DDSCAPS3_MULTISAMPLE_QUALITY_MASK | DDSCAPS3_MULTISAMPLE_MASK |
                                       DDSCAPS3_RESERVED1                | DDSCAPS3_RESERVED2        |
                                       DDSCAPS3_LIGHTWEIGHTMIPMAP        | DDSCAPS3_AUTOGENMIPMAP    |
                                       DDSCAPS3_DMAP))
            {
                retVal = DDERR_INVALIDCAPS;
                _SEH2_LEAVE;
            }

            if ( ddscaps->dwCaps4)
            {
                retVal = DDERR_INVALIDCAPS;
                _SEH2_LEAVE;
            }

            ZeroMemory(&memdata, sizeof(DDHAL_GETAVAILDRIVERMEMORYDATA));
            memdata.lpDD = This->lpLcl->lpGbl;
            memdata.ddRVal = DDERR_INVALIDPARAMS;

            memdata.ddsCapsEx.dwCaps2 = ddscaps->dwCaps2;
            memdata.ddsCapsEx.dwCaps3 = ddscaps->dwCaps3;

            This->lpLcl->lpGbl->hDD = This->lpLcl->hDD;

            if (This->lpLcl->lpDDCB->HALDDMiscellaneous.GetAvailDriverMemory(&memdata) == DDHAL_DRIVER_NOTHANDLED)
            {
                retVal = DDERR_NODIRECTDRAWHW;

                if (dwTotal)
                    *dwTotal = 0;

                if (dwFree)
                    *dwFree = 0;
            }
            else
            {
                if (dwTotal)
                    *dwTotal = memdata.dwTotal;

                if (dwFree)
                    *dwFree = memdata.dwFree;

                retVal = memdata.ddRVal;
            }
        }
    }
    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
    {
    }
    _SEH2_END;

    return retVal;
}

HRESULT WINAPI
Main_DirectDraw_GetFourCCCodes(LPDDRAWI_DIRECTDRAW_INT This, LPDWORD lpNumCodes, LPDWORD lpCodes)
{
    HRESULT retVal = DD_OK;

    DX_WINDBG_trace();


     // EnterCriticalSection(&ddcs);

    _SEH2_TRY
    {
        if(IsBadWritePtr(lpNumCodes,sizeof(LPDWORD)))
        {
            retVal = DDERR_INVALIDPARAMS;
        }
        else
        {
            if(!(IsBadWritePtr(lpNumCodes,sizeof(LPDWORD))))
            {
                DWORD size;

                if (*lpNumCodes > This->lpLcl->lpGbl->dwNumFourCC)
                {
                    *lpNumCodes = This->lpLcl->lpGbl->dwNumFourCC;
                }

                size =  *lpNumCodes * sizeof(DWORD);

                if(!IsBadWritePtr(lpCodes, size ))
                {
                    memcpy(lpCodes, This->lpLcl->lpGbl->lpdwFourCC, size );
                }
                else
                {
                    *lpNumCodes = This->lpLcl->lpGbl->dwNumFourCC;
                }
            }
        }
    }
    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
    {
    }
    _SEH2_END;

    //LeaveCriticalSection(&ddcs);
    return retVal;
}


/*
 * We can obtain the version of the directdraw object by compare the
 * vtl table pointer from iface we do not need pass which version
 * we whant to use
 *
 * Main_DirectDraw_CreateSurface is dead at moment we do only support
 * directdraw 7 at moment
 */

/* For DirectDraw 1 - 3 */
HRESULT WINAPI
Main_DirectDraw_CreateSurface (LPDDRAWI_DIRECTDRAW_INT This, LPDDSURFACEDESC pDDSD,
                               LPDDRAWI_DDRAWSURFACE_INT *ppSurf, IUnknown *pUnkOuter)
{
   HRESULT ret = DDERR_GENERIC;
   DDSURFACEDESC2 dd_desc_v2;

   DX_WINDBG_trace();

    EnterCriticalSection(&ddcs);
    *ppSurf = NULL;

    _SEH2_TRY
    {
        if (pDDSD->dwSize == sizeof(DDSURFACEDESC))
        {
            CopyDDSurfDescToDDSurfDesc2(&dd_desc_v2, (LPDDSURFACEDESC)pDDSD);
            ret = Internal_CreateSurface(This,
                                         &dd_desc_v2,
                                         ppSurf,
                                         pUnkOuter);
        }
        else
        {
            ret = DDERR_INVALIDPARAMS;
        }
    }
    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
    {
        ret = DDERR_INVALIDPARAMS;
    }
    _SEH2_END;
    LeaveCriticalSection(&ddcs);
    return ret;
}


/* For DirectDraw 4 - 7 */
HRESULT WINAPI
Main_DirectDraw_CreateSurface4(LPDDRAWI_DIRECTDRAW_INT This, LPDDSURFACEDESC2 pDDSD,
                               LPDDRAWI_DDRAWSURFACE_INT *ppSurf, IUnknown *pUnkOuter)
{
    HRESULT ret = DD_OK;
    DX_WINDBG_trace();

    EnterCriticalSection(&ddcs);
    *ppSurf = NULL;

    _SEH2_TRY
    {
        ret = Internal_CreateSurface(This, pDDSD, ppSurf, pUnkOuter);
    }
    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
    {
        ret = DDERR_INVALIDPARAMS;
    }
    _SEH2_END;

    if(*ppSurf != NULL)
        Main_DirectDraw_AddRef(This);

    LeaveCriticalSection(&ddcs);
    return ret;
}

/* 5 of 31 DirectDraw7_Vtable api are working similar to windows */
/* 8 of 31 DirectDraw7_Vtable api are under devloping / testing */

HRESULT WINAPI Main_DirectDraw_CreatePalette(LPDDRAWI_DIRECTDRAW_INT This, DWORD dwFlags,
                  LPPALETTEENTRY palent, LPDIRECTDRAWPALETTE* ppPalette, LPUNKNOWN pUnkOuter)
{
	HRESULT ret = DD_OK;
    DX_WINDBG_trace();

    EnterCriticalSection(&ddcs);
    *ppPalette = NULL;

    _SEH2_TRY
    {
        ret = Internal_CreatePalette(This, dwFlags, palent, ppPalette, pUnkOuter);
    }
    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
    {
        ret = DDERR_INVALIDPARAMS;
    }
    _SEH2_END;

    //Versions 7 and 4 are addref'ed
    if((This->lpVtbl == &DirectDraw7_Vtable || This->lpVtbl == &DirectDraw4_Vtable) && *ppPalette != NULL)
        Main_DirectDraw_AddRef(This) ;

    LeaveCriticalSection(&ddcs);
    return ret;
}







