////////////////////////////////////////////////////////////////////////////////
// DirectDrawSprites.cpp
//
//  DirectDraw, sprites

#include <windows.h>
#include <ddraw.h>

#include "ddutil.h"

HWND hWndMain;
IDirectDraw7        *pDD;
IDirectDrawSurface7 *lpPrimary, *lpBackBuffer;

// Surface containing our background.
IDirectDrawSurface7 *lpA;
// Surface containing our sprites.
IDirectDrawSurface7 *lpSprites;

// Initialise DirectDraw and go to full screen mode.
void InitDirectDraw()
{
    // Create the DirectDraw object, through which we will create surfaces.
    DirectDrawCreateEx(NULL, (void **)&pDD, IID_IDirectDraw7, NULL);

    // Set the co-operative level to exclusive and full-screen.
    pDD->SetCooperativeLevel(hWndMain, 
        DDSCL_FULLSCREEN | DDSCL_EXCLUSIVE | DDSCL_ALLOWREBOOT);

    // Set the display mode to 640x480, 16-bit colour, default refresh rate.
    pDD->SetDisplayMode(640, 480, 16, 0, 0);

    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;
    pDD->CreateSurface(&ddsd, &lpPrimary, NULL);
    
    // Get the back buffer pointer, to which we will do all drawing.
    ddsd.ddsCaps.dwCaps = DDSCAPS_BACKBUFFER;
    lpPrimary->GetAttachedSurface(&ddsd.ddsCaps, &lpBackBuffer);

    // Load our background.
    lpA = DDLoadBitmap(pDD, "a.bmp", 0, 0);

    // Load the sprites.
    lpSprites = DDLoadBitmap(pDD, "sprites.bmp", 0, 0);
    DDSetColorKey(lpSprites, RGB(255, 0, 255));
}

void ExitDirectDraw()
{
    // Release our sprites surface.
    lpSprites->Release();
    lpSprites = NULL;

    // Release our background surface.
    lpA->Release();
    lpA = NULL;

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

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

// Handle all messages for the main window.
LRESULT CALLBACK MainWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
    case WM_CLOSE:
        ExitDirectDraw();
        DestroyWindow(hWnd);
        return 0;

    // Clear the cursor while active.
    case WM_SETCURSOR:
        SetCursor(NULL);
        return 0;

    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    }
    return DefWindowProc(hWnd, uMsg, wParam, lParam);
}

// Register the window class for the main window.
void RegisterWindowClass()
{
    WNDCLASSEX  wcx;

    ZeroMemory(&wcx, sizeof(WNDCLASSEX));
    wcx.cbSize = sizeof(WNDCLASSEX);
    wcx.lpfnWndProc = MainWindowProc;
    wcx.hInstance = GetModuleHandle(NULL);
    // Windows-default icon. Replace with your own.
    wcx.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wcx.hCursor = NULL;
    // Black background for the window.
    wcx.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
    wcx.lpszMenuName = NULL;
    wcx.lpszClassName = "SampleWindowClass";
    RegisterClassEx(&wcx);
}

// Idle-time processing function.

void OnIdle(void)
{
    if (lpPrimary == NULL)
        return;

    RECT    r;
    
    // Draw the background to the screen.
    r.left = 0;
    r.top = 0;
    r.right = 640;
    r.bottom = 480;
    lpBackBuffer->BltFast(0, 0, lpA, &r, 
        DDBLTFAST_NOCOLORKEY | DDBLTFAST_WAIT);

    // Draw a sprite in the middle of the screen.
    r.left = 128;
    r.top = 0;
    r.right = 192;
    r.bottom = 64;
    lpBackBuffer->BltFast(288, 208, lpSprites, &r, 
        DDBLTFAST_SRCCOLORKEY | DDBLTFAST_WAIT);

    // Move a sprite around automatically.
    static int x;

    x++;
    if (x > 100)
        x = -100;
    
    r.left = 0;
    r.right = 64;
    lpBackBuffer->BltFast(288 + x, 100, lpSprites, &r, 
        DDBLTFAST_SRCCOLORKEY | DDBLTFAST_WAIT);

    // Move a sprite around with the mouse.
    POINT p;
    GetCursorPos(&p);

    r.left = 64;
    r.right = 128;
    lpBackBuffer->BltFast(p.x, p.y, lpSprites, &r, 
        DDBLTFAST_SRCCOLORKEY | DDBLTFAST_WAIT); 

    lpPrimary->Flip(NULL, DDFLIP_WAIT);
}

// Program entry point.

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int) 
{
    MSG msg;

    RegisterWindowClass();

    // Create a window that fills the screen.
    hWndMain = CreateWindowEx(WS_EX_APPWINDOW,
        "SampleWindowClass", "DirectDraw Sprites Sample", WS_POPUP,
        0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN),
        NULL, NULL, hInstance, NULL);
    
    ShowWindow(hWndMain, SW_SHOW);

    InitDirectDraw();

    // Message loop. Note that this has been modified to allow
    //  us to execute even if no messages are being processed.
    for (;;)
    {
        if (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE))
        {
            if (!GetMessage(&msg, NULL, 0, 0))
                break;
            
            // If you want WM_CHAR messages, add
            //  TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
        
        // Idle-time processing
        OnIdle();
    }

    return 0;
}