////////////////////////////////////////////////////////////////////////////////
// Draw.cpp
//
//  DirectDraw handling.

#include "stdhdr.h"

#include "Debug.h"
#include "Draw.h"
#include "ddutil.h"

// Force the inclusion of the libraries we need
#pragma comment(lib, "kernel32.lib")
#pragma comment(lib, "user32.lib")
#pragma comment(lib, "gdi32.lib")
#pragma comment(lib, "dxguid.lib")
#pragma comment(lib, "ddraw.lib")

IDirectDraw7        *g_pDD;
IDirectDrawSurface7 *g_lpPrimary, *g_lpBackBuffer;

static  int g_cxScreen, g_cyScreen, g_nBPP;

// Initialise DirectDraw and go to full screen mode.
BOOL InitDirectDraw(int cxScreen, int cyScreen, int nBPP)
{
    HRESULT h;

    // Create the DirectDraw object, through which we will create surfaces.
    h = DirectDrawCreateEx(NULL, (void **)&g_pDD, IID_IDirectDraw7, NULL);
    if (FAILED(h))
    {
        DISPLAYERRORCODE(h, "DirectDraw could not be initialised. Check for low memory or low system resources.");
        return FALSE;
    }

    ShowWindow(hWndMain, SW_SHOW);

    // Set the co-operative level to exclusive and full-screen.
    g_pDD->SetCooperativeLevel(hWndMain, 
        DDSCL_FULLSCREEN | DDSCL_EXCLUSIVE | DDSCL_ALLOWREBOOT);
    if (FAILED(h))
    {
        ExitDirectDraw();

        DISPLAYERRORCODE(h, "DirectDraw could not be initialised. Check for low memory or low system resources.");
        return FALSE;
    }

    // Set the display mode
    g_cxScreen = cxScreen;
    g_cyScreen = cyScreen;
    g_nBPP = nBPP;
    h = g_pDD->SetDisplayMode(g_cxScreen, g_cyScreen, g_nBPP, 0, 0);
    if (FAILED(h))
    {
        ExitDirectDraw();
        
        DISPLAYERRORCODE(h, "This graphics card does not support the required video mode.");
        return FALSE;
    }

    DDSURFACEDESC2  ddsd;

    // Create the primary surface with two back buffers.
    ZeroMemory(&ddsd, sizeof(DDSURFACEDESC2));
    ddsd.dwSize = sizeof(DDSURFACEDESC2);
    ddsd.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;
    ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE |
            DDSCAPS_FLIP |
            DDSCAPS_COMPLEX;
    ddsd.dwBackBufferCount = 2;
    h = g_pDD->CreateSurface(&ddsd, &g_lpPrimary, NULL);
    if (FAILED(h))
    {
        ExitDirectDraw();
        
        DISPLAYERRORCODE(h, "This graphics card does not have enough video memory.");
        return FALSE;
    }
    
    // Get the back buffer pointer, to which we will do all drawing.
    ddsd.ddsCaps.dwCaps = DDSCAPS_BACKBUFFER;
    g_lpPrimary->GetAttachedSurface(&ddsd.ddsCaps, &g_lpBackBuffer);

    return TRUE;
}

IDirectDrawSurface7 *LoadSurface(LPCTSTR lpFilename)
{
    return DDLoadBitmap(g_pDD, lpFilename, 0, 0);
}

void ExitDirectDraw()
{
    // Release the primary surface. Note that the back buffer is automatically released 
    //  (since it was created at the same time).
    g_lpPrimary->Release();
    g_lpPrimary = NULL;

    // Release the DirectDraw object.
    g_pDD->Release();
    g_pDD = NULL;
}

BOOL BackBlt(int x, int y, IDirectDrawSurface7 *lpSurf, LPCRECT pRect, DWORD dwFlags)
{
    HRESULT h;
    RECT    rc = *pRect;

    // Clip against bottom and right edges
    if ((x > g_cxScreen) || (y > g_cyScreen))
        return TRUE;

    // Clip against left and top edges: make the rectangle smaller.
    if (x < 0)
    {   rc.left -= x;
        x = 0;
    }
    if (y < 0)
    {   rc.top -= y;
        y = 0;
    }
    
    if (rc.left < 0)
        rc.left = 0;
    if (rc.top < 0)
        rc.top = 0;
    
    // Clip against bottom and right edges: make the rectangle smaller.
    if ((x + rc.right - rc.left) > g_cxScreen)
        rc.right -= (x + rc.right - rc.left) - g_cxScreen;
    if ((y + rc.bottom - rc.top) > g_cyScreen)
        rc.bottom -= (y + rc.bottom - rc.top) - g_cyScreen;

    if ((rc.right <= rc.left) || (rc.bottom <= rc.top))
        return TRUE;
    
    h = g_lpBackBuffer->BltFast(x, y, lpSurf, &rc, dwFlags | DDBLTFAST_WAIT);

    if (h == DDERR_SURFACELOST)
    {   
        g_pDD->RestoreAllSurfaces();
        ReloadAllSurfaces();
        h = g_lpBackBuffer->BltFast(x, y, lpSurf, &rc, dwFlags | DDBLTFAST_WAIT);
    }
    
    if (h != DD_OK)
        return FALSE;
    return TRUE;
}

BOOL ClearScreen(DWORD dwColour)
{
    DDBLTFX ddfx;
    HRESULT h;

    ddfx.dwSize = sizeof(ddfx);
    ddfx.dwFillColor = dwColour;
    
    h = g_lpBackBuffer->Blt(NULL, NULL, NULL, DDBLT_COLORFILL | DDBLT_WAIT, &ddfx);
    
    if (h == DDERR_SURFACELOST)
    {   
        g_pDD->RestoreAllSurfaces();
        ReloadAllSurfaces();
        h = g_lpBackBuffer->Blt(NULL, NULL, NULL, DDBLT_COLORFILL | DDBLT_WAIT, &ddfx);
    }
    
    if (h != DD_OK)
        return FALSE;
    return TRUE;
}