//-----------------------------------------------------------------------------
// File: WinMain.CPP
//
// Desc: This sample demonstrates how to bring up a dialog, or any type of
//       window, in a DirectDraw full-screen exclusive mode, even on non GDI
//       devices.
//
//
// Copyright (c) 1998-1999 Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------

#define NAME    "FSWindow"
#define TITLE   "DDraw Full-Screen Dialog Example"

#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif

//-----------------------------------------------------------------------------
// Include files
//-----------------------------------------------------------------------------
#include "dconfigp.h"

BOOL CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
BOOL Init();

//-----------------------------------------------------------------------------
// Global Variables
//-----------------------------------------------------------------------------
extern HINSTANCE                   g_hInst;
IDirectDraw7               *g_pDD = NULL;       // DirectDraw object
IDirectDrawSurface7        *g_pDDSPrimary = NULL;  // DirectDraw primary surface
IDirectDrawSurface7        *g_pDDSBack = NULL;  // DirectDraw back surface
BOOL                        g_fActive = FALSE;  // App is running/active
BOOL                        g_fReady = FALSE;   // App is ready for updates
BOOL                        g_fPaused = FALSE;  // App is paused
HWND                        g_hWndHelp = NULL;
HWND                        g_hWndDlg = NULL;   // Sample dialog box handle


//-----------------------------------------------------------------------------
// Name: DebugMsg()
// Desc: This function essentially is a printf for debug output.
//-----------------------------------------------------------------------------
void
DebugMsg(LPCTSTR szError,...)
{
    char                        szBuff[128];
    va_list                     vl;

    va_start(vl, szError);
#ifdef WIN95
	{
		char *psz = NULL;
		char szDfs[1024]={0};
		strcpy(szDfs,szError);					// make a local copy of format string
		while (psz = strstr(szDfs,"%p"))		// find each %p
			*(psz+1) = 'x';						// replace each %p with %x
	    vsprintf(szBuff, szDfs, vl);    		// use the local format string
	}
#else
	{
	    vsprintf(szBuff, szError, vl);
	}
#endif
    OutputDebugString(szBuff);
    va_end(vl);
}




//-----------------------------------------------------------------------------
// Name: ReleaseAllObjects()
// Desc: Release all DDraw objects we use.
//-----------------------------------------------------------------------------
HRESULT
ReleaseAllObjects(HWND hWnd)
{
    if (g_pDD != NULL)
    {
        g_pDD->SetCooperativeLevel(hWnd, DDSCL_NORMAL);
        if (g_pDDSBack != NULL)
        {
            g_pDDSBack->Release();
            g_pDDSBack = NULL;
        }
        if (g_pDDSPrimary != NULL)
        {
            g_pDDSPrimary->Release();
            g_pDDSPrimary = NULL;
        }
    }
    return DD_OK;
}




//-----------------------------------------------------------------------------
// Name: InitFail()
// Desc: This function is called if an initialization function fails.
//-----------------------------------------------------------------------------
HRESULT
InitFail(HWND hWnd, HRESULT hRet, LPCTSTR szError,...)
{
    char                        szBuff[128];
    va_list                     vl;

    va_start(vl, szError);
#ifdef WIN95
	{
		char *psz = NULL;
		char szDfs[1024]={0};
		strcpy(szDfs,szError);					// make a local copy of format string
		while (psz = strstr(szDfs,"%p"))		// find each %p
			*(psz+1) = 'x';						// replace each %p with %x
	    vsprintf(szBuff, szDfs, vl);    		// use the local format string
	}
#else
	{
	    vsprintf(szBuff, szError, vl);
	}
#endif
    ReleaseAllObjects(hWnd);
    MessageBox(hWnd, szBuff, TITLE, MB_OK);
    DestroyWindow(hWnd);
    va_end(vl);
    return hRet;
}


//-----------------------------------------------------------------------------
// Name: AppPause()
// Desc: Toggles the pause mode of the app.
//-----------------------------------------------------------------------------
void
AppPause(HWND hWnd, BOOL fPause)
{
    if (fPause)
    {
        g_fPaused = TRUE;
        g_pDD->FlipToGDISurface();
        RedrawWindow(hWnd, NULL, NULL, RDW_FRAME);
    }
    else
    {
        RedrawWindow(hWnd, NULL, NULL, RDW_FRAME);
        g_fPaused = FALSE;
    }
}




//-----------------------------------------------------------------------------
// Name: InitSurfaces()
// Desc: Create all the needed DDraw surfaces and set the coop level.
//-----------------------------------------------------------------------------
static                      HRESULT
InitSurfaces(HWND hWnd)
{
    HRESULT                     hRet;
    DDSURFACEDESC2              ddsd;
    DDSCAPS2                    ddscaps;

    // Get exclusive mode
    hRet = g_pDD->SetCooperativeLevel(hWnd, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN);
    if (hRet != DD_OK)
        return InitFail(hWnd, hRet, "SetCooperativeLevel FAILED");

    // Set the video mode to 640x480x16
    hRet = g_pDD->SetDisplayMode(1024, 768, 16, 0, 0);
    if (hRet != DD_OK)
        return InitFail(hWnd, hRet, "SetDisplayMode FAILED");

    // Create the primary surface with 1 back buffer
    ZeroMemory(&ddsd, sizeof(ddsd));
    ddsd.dwSize = sizeof(ddsd);
    ddsd.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;
    ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_FLIP | DDSCAPS_COMPLEX;
    ddsd.dwBackBufferCount = 1;
    hRet = g_pDD->CreateSurface(&ddsd, &g_pDDSPrimary, NULL);
    if (hRet != DD_OK)
        return InitFail(hWnd, hRet, "CreateSurface (3) FAILED");

    ZeroMemory(&ddscaps, sizeof(ddscaps));
    ddscaps.dwCaps = DDSCAPS_BACKBUFFER;
    hRet = g_pDDSPrimary->GetAttachedSurface(&ddscaps, &g_pDDSBack);
    if (hRet != DD_OK)
        return InitFail(hWnd, hRet, "GetAttachedSurface FAILED");

    // Do help initialization
     FSWindow_Init(hWnd, g_pDD, g_pDDSPrimary, g_pDDSBack);

    return DD_OK;
}




//-----------------------------------------------------------------------------
// Name: ChangeCoopLevel()
// Desc: Called when the user wants to toggle between Full-Screen & Windowed.
//-----------------------------------------------------------------------------
HRESULT
ChangeCoopLevel(HWND hWnd)
{
    HRESULT                     hRet;

    // Release all objects that need to be re-created for the new device
    if (FAILED(hRet = ReleaseAllObjects(hWnd)))
        return InitFail(hWnd, hRet, "ReleaseAllObjects FAILED");

    // Re-create the surfaces
    hRet = InitSurfaces(hWnd);
    return hRet;
}

//-----------------------------------------------------------------------------
// Name: RenderFrame()
// Desc: Blts and moves a bouncing ball, as well as displays helpful text.
//-----------------------------------------------------------------------------
BOOL
RenderFrame()
{
    DDBLTFX                     ddbltfx;
    HDC                         hDC;
    static int                  x1 = 0,
                                y1 = 0,
                                x2 = 40,
                                y2 = 40;
    HBRUSH                      hOldBrush;
    HPEN                        hOldPen;
    static int                  xDir = +8,
                                yDir = +8;

    // Use the blter to do a color fill to clear the back buffer
    ZeroMemory(&ddbltfx, sizeof(ddbltfx));
    ddbltfx.dwSize = sizeof(ddbltfx);
    ddbltfx.dwFillColor = 0;
    g_pDDSBack->Blt(NULL, NULL, NULL, DDBLT_COLORFILL | DDBLT_WAIT, &ddbltfx);

    if (g_pDDSBack->GetDC(&hDC) == DD_OK)
    {
        // Paint the bouncing ball
        SetBkColor(hDC, RGB(0, 0, 255));
        SetTextColor(hDC, RGB(255, 255, 0));
        hOldBrush = (HBRUSH) SelectObject(hDC, GetStockObject(LTGRAY_BRUSH));
        hOldPen = (HPEN) SelectObject(hDC, GetStockObject(WHITE_PEN));
        Ellipse(hDC, x1, y1, x2, y2);
        SelectObject(hDC, hOldPen);
        SelectObject(hDC, hOldBrush);

        // Move the bouncing ball and make it bounce
        x1 += xDir;
        x2 += xDir;
        if (x1 < 0)
        {
            x1 = 0;
            x2 = 40;
            xDir = -xDir;
        }
        if (x2 >= 640)
        {
            x1 = 640 - 1 - 40;
            x2 = 640 - 1;
            xDir = -xDir;
        }
        y1 += yDir;
        y2 += yDir;
        if (y1 < 0)
        {
            y1 = 0;
            y2 = 40;
            yDir = -yDir;
        }
        if (y2 >= 480)
        {
            y1 = 480 - 1 - 40;
            y2 = 480 - 1;
            yDir = -yDir;
        }

        // Display the proper text
        TextOut(hDC, 0, 0, "Press Escape to quit", 20);
        TextOut(hDC, 0, 20, "Press F1 to bring up the dialog", 31);

        g_pDDSBack->ReleaseDC(hDC);
    }
    return TRUE;
}




//-----------------------------------------------------------------------------
// Name: UpdateFrame()
// Desc: Call RenderFrame() and the BLT and/or flip the new frame.
//-----------------------------------------------------------------------------
void
UpdateFrame()
{
    HRESULT                     hRet;

    // Update the background and flip
    //RenderFrame();
    while (TRUE)
    {
        if (FSWindow_IsActive())
        {
            FSWindow_Update();
            break;
        }
        else
        {
            // Else we are in fullscreen mode, so perform a flip.
            hRet = g_pDDSPrimary->Flip(NULL, 0L);
        }
        if (hRet == DD_OK)
            break;
        if (hRet == DDERR_SURFACELOST)
        {
            hRet = g_pDDSPrimary->Restore();
            if (hRet != DD_OK)
                break;
        }
        if (hRet != DDERR_WASSTILLDRAWING)
            break;
    }
}




//-----------------------------------------------------------------------------
// Name: MainWndProc()
// Desc: The Main Window Procedure.
//-----------------------------------------------------------------------------
LRESULT CALLBACK
MainWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg)
    {
        case WM_ACTIVATEAPP:
            // Pause if minimized
            g_fActive = !((BOOL)HIWORD(wParam));
            return 0L;

        case WM_CLOSE:
             FSWindow_End();
            DestroyWindow(hWnd);
            break;

        case WM_DESTROY:
            // Clean up and close the app
            ReleaseAllObjects(hWnd);
            PostQuitMessage(0);
            return 0L;

        case WM_KEYDOWN:
            // Handle any non-accelerated key commands
            switch (wParam)
            {
                case VK_PAUSE:
                    // Allow the app to be paused
                    AppPause(hWnd, !g_fPaused);
                    break;
                case VK_ESCAPE:
                case VK_F12:
                    PostMessage(hWnd, WM_CLOSE, 0, 0);
                    return 0L;
                case VK_F1:
                     
                     g_hWndHelp = FSWindow_Begin(g_hWndDlg, FALSE);
                     g_hWndDlg = CreateDialog(g_hInst,
                                              MAKEINTRESOURCE(IDD_DCONFIG_DIALOG),
                                              hWnd, (DLGPROC) DialogProc);
                     
                     ShowWindow(g_hWndDlg, SW_SHOWNORMAL);
                     break;
            }
            break;

        case WM_SETCURSOR:
            // Display the cursor if conditions are right
            if (g_fActive && g_fReady && !g_fPaused && !FSWindow_IsActive())
            {
                SetCursor(NULL);
                return TRUE;
            }
            break;
    }
    return DefWindowProc(hWnd, msg, wParam, lParam);
}


//-----------------------------------------------------------------------------
// Name: WinMain()
// Desc: Entry point to the program. Initializes everything and calls
//       AppIdle() when idle from the message pump.
//-----------------------------------------------------------------------------
int                         PASCAL
WinMain(HINSTANCE hInstance,
        HINSTANCE hPrevInstance,
        LPSTR lpszCmdLine,
        int nCmdShow)
{
    WNDCLASS                    wc;
    MSG                         msg;
    HWND                        hWnd;
    HRESULT                     hRet;
    HINSTANCE                   hDDrawDLL;
    LPDIRECTDRAWENUMERATEEX     pDirectDrawEnumerateEx;
    DWORD                       dwLastTime;
    DWORD                       dwThisTime;

    if (!hPrevInstance)
    {
        // Register the Window Class
        wc.lpszClassName = NAME;
        wc.lpfnWndProc = MainWndProc;
        wc.style = CS_VREDRAW | CS_HREDRAW;
        wc.hInstance = hInstance;
        wc.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_DCONFIG_ICON));
        wc.hCursor = LoadCursor(NULL, IDC_ARROW);
        wc.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
        wc.lpszMenuName = NULL;
        wc.cbClsExtra = 0;
        wc.cbWndExtra = 0;
        if (!RegisterClass(&wc))
            return FALSE;
    }

    g_hInst = g_hInst = hInstance;
    
//    Init();
    // Create and Show the Main Window
    hWnd = CreateWindowEx(0,
                          NAME,
                          TITLE,
                          WS_CLIPCHILDREN | WS_POPUP,
                          CW_USEDEFAULT,
                          CW_USEDEFAULT,
                          1024,
                          768,
                          NULL,
                          NULL,
                          hInstance,
                          NULL);
    if (hWnd == NULL)
        return FALSE;

    // You need to do a GetModuleHandle and GetProcAddress in order to get the
    // DirectDrawEnumerateEx function pointer
     hDDrawDLL = GetModuleHandle("DDRAW");
     if (!hDDrawDLL)
        return InitFail(hWnd, DD_OK, "GetModuleHandle FAILED");

     //Create the main DirectDraw object
     hRet = DirectDrawCreateEx(NULL, (VOID**)&g_pDD, IID_IDirectDraw7, NULL);
     if (hRet != DD_OK)
        return InitFail(hWnd, hRet, "DirectDrawCreateEx FAILED");

     //Initialize all the surfaces we need
     hRet = InitSurfaces(hWnd);
     if (FAILED(hRet))
        return FALSE;

    g_fReady = TRUE;

    g_hWndDlg = CreateDialog(g_hInst, MAKEINTRESOURCE(IDD_DCONFIG_DIALOG),
                         hWnd, (DLGPROC) DialogProc);

    ShowWindow(g_hWndDlg, SW_SHOWNORMAL);
    Sleep(1000);

    g_hWndHelp = FSWindow_Begin(g_hWndDlg, FALSE);
     // Put up the sample dialog right away, to show the sample user what this
     // sample is trying to demonstrate.

    //-------------------------------------------------------------------------
    //                          The Message Pump
    //-------------------------------------------------------------------------
    dwLastTime = 0;
    dwThisTime = timeGetTime();
    while (TRUE)
    {
        if (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE))
        {
            if (!GetMessage(&msg, NULL, 0, 0))
                break;
            // If the dialog is showing, translate messages for it since it's
            // a modeless dialog.
            if (g_hWndDlg == (HWND )NULL || !IsDialogMessage(g_hWndDlg, &msg))
            {
                // Translate and dispatch the message
                TranslateMessage(&msg);
                DispatchMessage(&msg);
            }
        }
        else
            if (g_fActive && g_fReady && !g_fPaused)
            {
                dwThisTime = timeGetTime();
                // 1000/FPS = number of milliseconds to wait
                if ((dwThisTime - dwLastTime) >= (1000/30))
                {
                    UpdateFrame();
                    dwLastTime = timeGetTime();
                }
            }
            else
                // Make sure we go to sleep if we have nothing else to do
                WaitMessage();
    }
    // Release the main DDraw interface
    if (g_pDD)
        g_pDD->Release();

    return msg.wParam;
}