/*
 * PROJECT:     ReactX Diagnosis Application
 * LICENSE:     LGPL - See COPYING in the top level directory
 * PURPOSE:     ReactX DirectDraw tests
 * COPYRIGHT:   Copyright 2008 Kamil Horníček
 *
 */

#include "precomp.h"

BOOL DDPrimarySurfaceTest(GUID *lpDevice, HWND hWnd);
BOOL DDOffscreenBufferTest(GUID *lpDevice, HWND hWnd, BOOL Fullscreen);
VOID DDRedrawFrame(LPDIRECTDRAWSURFACE lpDDSurface);
VOID DDUpdateFrame(LPDIRECTDRAWSURFACE lpDDPrimarySurface ,LPDIRECTDRAWSURFACE lpDDBackBuffer, BOOL Fullscreen, INT *posX, INT *posY, INT *gainX, INT *gainY, RECT *rectDD);

#define TEST_DURATION 10000
#define DD_TEST_WIDTH 640
#define DD_TEST_HEIGHT 480
#define DD_TEST_STEP 5
#define DD_SQUARE_SIZE 100
#define DD_SQUARE_STEP 2


BOOL StartDDTest(GUID *lpDevice, HWND hWnd, HINSTANCE hInstance, INT resTestDescription, INT resResult, INT TestNr)
{
    WCHAR szTestDescription[256];
    WCHAR szCaption[256];
    WCHAR szResult[256];
    WCHAR szError[256];
    BOOL Result;

    LoadStringW(hInstance, IDS_MAIN_DIALOG, szCaption, sizeof(szCaption) / sizeof(WCHAR));
    LoadStringW(hInstance, IDS_DDTEST_ERROR, szError, sizeof(szError) / sizeof(WCHAR));
    LoadStringW(hInstance, resTestDescription, szTestDescription, sizeof(szTestDescription) / sizeof(WCHAR));
    LoadStringW(hInstance, resResult, szResult, sizeof(szResult) / sizeof(WCHAR));

    if(MessageBox(NULL, szTestDescription, szCaption, MB_YESNO | MB_ICONQUESTION) == IDNO)
        return FALSE;

    ShowWindow(hWnd, SW_SHOW);

    switch(TestNr){
        case 1:
            Result = DDPrimarySurfaceTest(lpDevice, hWnd);
            break;
        case 2:
            Result = DDOffscreenBufferTest(lpDevice, hWnd, FALSE);
            break;
        case 3:
            Result = DDOffscreenBufferTest(lpDevice, hWnd, TRUE);
            break;
        default:
            Result = FALSE;
    }

    ShowWindow(hWnd, SW_HIDE);

    if(!Result)
    {
        MessageBox(NULL, szError, szCaption, MB_OK | MB_ICONERROR);
        return FALSE;
    }

    if(MessageBox(NULL, szResult, szCaption, MB_YESNO | MB_ICONQUESTION) == IDYES)
        return TRUE;

    return FALSE;
}

VOID DDTests(GUID *lpDevice)
{
    WNDCLASSEX winClass;
    HWND hWnd;
    HINSTANCE hInstance = NULL;
    WCHAR szDescription[256];
    WCHAR szCaption[256];

    winClass.cbSize = sizeof(WNDCLASSEX);
    winClass.style = CS_DBLCLKS | CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
    winClass.lpfnWndProc = DefWindowProc;
    winClass.cbClsExtra = 0;
    winClass.cbWndExtra = 0;
    winClass.hInstance = hInstance;
    winClass.hIcon = 0;
    winClass.hCursor = 0;
    winClass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
    winClass.lpszMenuName = NULL;
    winClass.lpszClassName = L"ddtest";
    winClass.hIconSm = LoadIcon(NULL, IDI_APPLICATION);

    if (!RegisterClassEx(&winClass))
        return;

    hWnd = CreateWindowEx(0, winClass.lpszClassName, NULL,WS_POPUP,
                          (GetSystemMetrics(SM_CXSCREEN) - DD_TEST_WIDTH)/2,
                          (GetSystemMetrics(SM_CYSCREEN) - DD_TEST_HEIGHT)/2,
                          DD_TEST_WIDTH, DD_TEST_HEIGHT, NULL, NULL, hInstance, NULL);

    if (!hWnd){
        return;
    }

    LoadStringW(hInstance, IDS_DDTEST_DESCRIPTION, szDescription, sizeof(szDescription) / sizeof(WCHAR));
    LoadStringW(hInstance, IDS_MAIN_DIALOG, szCaption, sizeof(szCaption) / sizeof(WCHAR));
    if(MessageBox(NULL, szDescription, szCaption, MB_YESNO | MB_ICONQUESTION) == IDNO)
        return;

    StartDDTest(lpDevice, hWnd, hInstance, IDS_DDPRIMARY_DESCRIPTION, IDS_DDPRIMARY_RESULT, 1);
    StartDDTest(lpDevice, hWnd, hInstance, IDS_DDOFFSCREEN_DESCRIPTION, IDS_DDOFFSCREEN_RESULT, 2);
    StartDDTest(lpDevice, hWnd, hInstance, IDS_DDFULLSCREEN_DESCRIPTION, IDS_DDFULLSCREEN_RESULT, 3);

    DestroyWindow(hWnd);
    UnregisterClass(winClass.lpszClassName, hInstance);
}

BOOL DDPrimarySurfaceTest(GUID *lpDevice, HWND hWnd){
    UINT TimerID;
    MSG msg;

    LPDIRECTDRAW lpDD = NULL;
    LPDIRECTDRAWSURFACE lpDDSurface = NULL;
    DDSURFACEDESC DDSurfaceDesc;

    if(DirectDrawCreate(lpDevice, &lpDD, NULL) != DD_OK)
        return FALSE;

    if(lpDD->lpVtbl->SetCooperativeLevel(lpDD, hWnd, DDSCL_NORMAL) != DD_OK)
    {
        lpDD->lpVtbl->Release(lpDD);
        return FALSE;
    }

    /* create our primary surface */
    ZeroMemory(&DDSurfaceDesc, sizeof(DDSurfaceDesc));
    DDSurfaceDesc.dwSize = sizeof(DDSurfaceDesc);
    DDSurfaceDesc.dwFlags = DDSD_CAPS;
    DDSurfaceDesc.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_3DDEVICE;
    DDSurfaceDesc.dwBackBufferCount = 0;

    if(lpDD->lpVtbl->CreateSurface(lpDD, &DDSurfaceDesc, &lpDDSurface, NULL) != DD_OK)
    {
        lpDD->lpVtbl->Release(lpDD);
        return FALSE;
    }

    TimerID = SetTimer(hWnd, -1, (UINT)TEST_DURATION, NULL);

    while (TRUE)
    {
        if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
        {
            if (msg.message == WM_TIMER && TimerID == msg.wParam)
                break;
            TranslateMessage(&msg);
            DispatchMessage(&msg);
            if (msg.message == WM_PAINT)
                DDRedrawFrame(lpDDSurface);
        }
    }
    KillTimer(hWnd, TimerID);
    lpDDSurface->lpVtbl->Release(lpDDSurface);
    lpDD->lpVtbl->Release(lpDD);

return TRUE;
}

VOID DDRedrawFrame(LPDIRECTDRAWSURFACE lpDDSurface)
{
    HDC hdc;

    if (lpDDSurface->lpVtbl->GetDC(lpDDSurface, &hdc) == DD_OK)
    {
        RECT rct;
        HBRUSH BlackBrush, WhiteBrush;
        BOOL Colour = FALSE;

        rct.left = (GetSystemMetrics(SM_CXSCREEN) - DD_TEST_WIDTH)/2;
        rct.right = (GetSystemMetrics(SM_CXSCREEN) - DD_TEST_WIDTH)/2 + DD_TEST_WIDTH;
        rct.top = (GetSystemMetrics(SM_CYSCREEN) - DD_TEST_HEIGHT)/2;
        rct.bottom = (GetSystemMetrics(SM_CYSCREEN) - DD_TEST_HEIGHT)/2 + DD_TEST_HEIGHT;

        BlackBrush = CreateSolidBrush(RGB(0,0,0));
        WhiteBrush = CreateSolidBrush(RGB(255,255,255));

        while((rct.bottom - rct.top) > DD_TEST_STEP){
            (Colour)? FillRect(hdc, &rct, WhiteBrush) : FillRect(hdc, &rct, BlackBrush);
            rct.top += DD_TEST_STEP;
            rct.bottom -= DD_TEST_STEP;
            rct.left += DD_TEST_STEP;
            rct.right -= DD_TEST_STEP;
            Colour = !Colour;
        }
        DeleteObject((HGDIOBJ)BlackBrush);
        DeleteObject((HGDIOBJ)WhiteBrush);
        lpDDSurface->lpVtbl->ReleaseDC(lpDDSurface, hdc);
    }
}


BOOL DDOffscreenBufferTest(GUID *lpDevice, HWND hWnd, BOOL Fullscreen){
    UINT_PTR TimerID, TimerIDUpdate;
    LPDIRECTDRAW lpDD;
    LPDIRECTDRAWSURFACE lpDDPrimarySurface;
    LPDIRECTDRAWSURFACE lpDDBackBuffer;
    DDSURFACEDESC DDSurfaceDesc;
    DDSURFACEDESC DDBBSurfaceDesc;
    DDSCAPS DDSCaps;
    MSG msg;
    RECT rectDD;
    POINT wndPoint;
    INT posX = 0, posY = 10, xGain = DD_SQUARE_STEP, yGain = DD_SQUARE_STEP;

    if(DirectDrawCreate(lpDevice, &lpDD, NULL) != DD_OK)
        return FALSE;

    ZeroMemory(&DDSurfaceDesc, sizeof(DDSurfaceDesc));
    DDSurfaceDesc.dwSize = sizeof(DDSurfaceDesc);

    if(Fullscreen)
    {
        if(lpDD->lpVtbl->SetCooperativeLevel(lpDD, hWnd, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN) != DD_OK)
        {
            lpDD->lpVtbl->Release(lpDD);
            return FALSE;
        }
        if(lpDD->lpVtbl->SetDisplayMode(lpDD, DD_TEST_WIDTH, DD_TEST_HEIGHT, 32) != DD_OK)
        {
            lpDD->lpVtbl->Release(lpDD);
            return FALSE;
        }
        DDSurfaceDesc.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;
        DDSurfaceDesc.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE  | DDSCAPS_FLIP | DDSCAPS_COMPLEX;
        DDSurfaceDesc.dwBackBufferCount = 1;
    }
    else
    {
        if(lpDD->lpVtbl->SetCooperativeLevel(lpDD, hWnd, DDSCL_NORMAL) != DD_OK)
        {
            lpDD->lpVtbl->Release(lpDD);
            return FALSE;
        }
        DDSurfaceDesc.dwFlags = DDSD_CAPS;
        DDSurfaceDesc.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
    }


    if(lpDD->lpVtbl->CreateSurface(lpDD, &DDSurfaceDesc, &lpDDPrimarySurface, NULL) != DD_OK)
    {
        lpDD->lpVtbl->Release(lpDD);
        return FALSE;
    }

    if(Fullscreen)
    {
        DDSCaps.dwCaps = DDSCAPS_BACKBUFFER;
        if (lpDDPrimarySurface->lpVtbl->GetAttachedSurface(lpDDPrimarySurface, &DDSCaps,&lpDDBackBuffer) != DD_OK)
        {
            lpDDPrimarySurface->lpVtbl->Release(lpDDPrimarySurface);
            lpDD->lpVtbl->Release(lpDD);
            return FALSE;
        }
    }
    else
    {
        /* create our offscreen back buffer */
        ZeroMemory(&DDBBSurfaceDesc,sizeof(DDBBSurfaceDesc));
        DDBBSurfaceDesc.dwSize = sizeof(DDBBSurfaceDesc);
        DDBBSurfaceDesc.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT;
        DDBBSurfaceDesc.dwHeight = DD_TEST_HEIGHT;
        DDBBSurfaceDesc.dwWidth = DD_TEST_WIDTH;
        DDBBSurfaceDesc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;

        if(lpDD->lpVtbl->CreateSurface(lpDD, &DDBBSurfaceDesc, &lpDDBackBuffer, NULL) != DD_OK)
        {
            lpDD->lpVtbl->Release(lpDD);
            lpDDPrimarySurface->lpVtbl->Release(lpDDPrimarySurface);
            return FALSE;
        }
        wndPoint.x = 0;
        wndPoint.y = 0;
        ClientToScreen(hWnd, &wndPoint);
        GetClientRect(hWnd, &rectDD);
        OffsetRect(&rectDD, wndPoint.x, wndPoint.y);
    }

    /* set our timers, TimerID - for test timeout, TimerIDUpdate - for frame updating */
    TimerID = SetTimer(hWnd, -1, (UINT)TEST_DURATION, NULL);
    TimerIDUpdate = SetTimer(hWnd, 2, (UINT)10, NULL);
    (void)TimerIDUpdate;

    while (TRUE)
    {
        if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
        {
            if (msg.message == WM_TIMER)
            {
                if(msg.wParam == TimerID)
                {
                    break;
                }
                else
                {
                    DDUpdateFrame(lpDDPrimarySurface,lpDDBackBuffer, Fullscreen,&posX, &posY, &xGain, &yGain, &rectDD);
                }
            }
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    lpDDPrimarySurface->lpVtbl->Release(lpDDPrimarySurface);
    /* backbuffer is released automatically when in fullscreen */
    if(!Fullscreen)
        lpDDBackBuffer->lpVtbl->Release(lpDDBackBuffer);
    lpDD->lpVtbl->Release(lpDD);

return TRUE;
}


VOID DDUpdateFrame(LPDIRECTDRAWSURFACE lpDDPrimarySurface ,LPDIRECTDRAWSURFACE lpDDBackBuffer, BOOL Fullscreen, INT *posX, INT *posY, INT *gainX, INT *gainY, RECT *rectDD)
{
    HDC hdc;
    DDBLTFX DDBlitFx;

    /* clear back buffer and paint it black */
    ZeroMemory(&DDBlitFx, sizeof(DDBlitFx));
    DDBlitFx.dwSize = sizeof(DDBlitFx);
    DDBlitFx.dwFillColor = 0;

    lpDDBackBuffer->lpVtbl->Blt(lpDDBackBuffer, NULL, NULL, NULL, DDBLT_COLORFILL | DDBLT_WAIT, &DDBlitFx);

    if (lpDDBackBuffer->lpVtbl->GetDC(lpDDBackBuffer, &hdc) == DD_OK)
    {
        RECT rct;
        HBRUSH WhiteBrush;

        rct.left = *posX;
        rct.right = *posX+DD_SQUARE_SIZE;
        rct.top = *posY;
        rct.bottom = *posY+DD_SQUARE_SIZE;

        WhiteBrush = CreateSolidBrush(RGB(255,255,255));
        FillRect(hdc, &rct, WhiteBrush);

        if(*posX >= (DD_TEST_WIDTH - DD_SQUARE_SIZE)) *gainX = -(*gainX);
        if(*posY >= (DD_TEST_HEIGHT - DD_SQUARE_SIZE)) *gainY = -(*gainY);
        if(*posX < 0) *gainX = -1*(*gainX);
        if(*posY < 0) *gainY = -1*(*gainY);

        *posX += *gainX;
        *posY += *gainY;

        lpDDBackBuffer->lpVtbl->ReleaseDC(lpDDBackBuffer, hdc);

        if(Fullscreen)
        {
            lpDDPrimarySurface->lpVtbl->Flip(lpDDPrimarySurface, NULL, DDFLIP_WAIT);
        }
        else
        {
            lpDDPrimarySurface->lpVtbl->Blt(lpDDPrimarySurface, rectDD, lpDDBackBuffer, NULL, DDBLT_WAIT, NULL);
        }
    }
}
