// $$BLANK$$.cpp - minimal D3D application
//
// Originally by Sameer Nene of Microsoft, used with permission.
// Modified by Rich Thomson
//
#include <windows.h>
#include <d3d8.h>
#include <tchar.h>

// width and height of frame buffer
const UINT FRAME_WIDTH = 320;
const UINT FRAME_HEIGHT = 240;

// device window and interface
HWND g_window = 0;
IDirect3DDevice8 *g_device = 0;

// device presentation parameters
D3DPRESENT_PARAMETERS g_presentation =
{
    // width, height, format, number of back buffers
    FRAME_WIDTH, FRAME_HEIGHT, D3DFMT_UNKNOWN, 1,
    // multisample type, swap effect, device window, windowed
    D3DMULTISAMPLE_NONE, D3DSWAPEFFECT_FLIP, g_window, TRUE,
    // auto depth/stencil surface flag, format
    FALSE, D3DFMT_UNKNOWN
};

// draw a frame consisting of
// a single white triangle on a black background
void
frame_draw()
{
    if (g_device)
    {
        // structure for triangle vertex positions
#define VTX(x_, y_) \
        { x_*FRAME_WIDTH, y_*FRAME_HEIGHT, 0.5f, 2.0f }
        const struct MYVERTEX
        {
            float m_pos[4];                 // x, y, z, 1/w
        }
        triangle[] =
        {
            VTX(0.50f, 0.25f),
            VTX(0.75f, 0.75f),
            VTX(0.25f, 0.75f)
        };
#undef VTX

        // start the scene and clear the frame buffer
        g_device->BeginScene();
        g_device->Clear(0, NULL, D3DCLEAR_TARGET, 0, 1.0f, 0);

        // describe triange vertex data to the device
        g_device->SetVertexShader(D3DFVF_XYZRHW);
        g_device->DrawPrimitiveUP(D3DPT_TRIANGLELIST, 1,
                                  triangle, sizeof(MYVERTEX));

        // end 3D scene
        g_device->EndScene();

        // present the scene & handle lost devices
        HRESULT hr = g_device->Present(NULL, NULL, NULL, NULL);
        while (D3DERR_DEVICELOST == hr)
        {
            do
            {
                ::Sleep(1000);
                hr = g_device->TestCooperativeLevel();
            }
            while (hr != D3DERR_DEVICENOTRESET);

            if (FAILED(g_device->Reset(&g_presentation)))
            {
                hr = D3DERR_DEVICELOST;
            }
        }

        // clear Win32 dirty region
        ::ValidateRect(g_window, NULL);
    }
}

// basic windows message procedure
LRESULT CALLBACK
frame_window_proc(HWND window, UINT msg, WPARAM wp, LPARAM lp)
{
    LRESULT result = 0;

    if (WM_SIZING == msg || WM_PAINT == msg)
    {
        frame_draw();
    }
    else
    {
        result = ::DefWindowProc(window, msg, wp, lp);
    }
    return result;
}

int __stdcall
_tWinMain(HINSTANCE instance, HINSTANCE, LPTSTR, int)
{
    const TCHAR *const APP_NAME = _T("$$BLANK$$");

    // Register the window class for the main window.
    {
        const WNDCLASS wc =
        {
            0, frame_window_proc, 0, 0, instance,
            NULL, LoadCursor(NULL, IDC_CROSS),
            static_cast<HBRUSH>(GetStockObject(BLACK_BRUSH)),
            NULL, APP_NAME
        };
        if (!::RegisterClass(&wc))
        {
            return 1;
        }
    }

    // Create the main window.
    g_window = ::CreateWindow(APP_NAME, APP_NAME,
        WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
        FRAME_WIDTH, FRAME_HEIGHT, ::GetDesktopWindow(), NULL,
        instance, NULL);
    if (!g_window)
    {
        return 1;
    }
    g_presentation.hDeviceWindow = g_window;

    {
        // Get IDirect3D8 interface
        IDirect3D8 *d3d = ::Direct3DCreate8(D3D_SDK_VERSION);
        if (!d3d)
        {
            ::DestroyWindow(g_window);
            return 1;
        }

        // find out display mode format to use for back buffer
        D3DDISPLAYMODE dm;
        HRESULT hr =
            d3d->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &dm);
        if (FAILED(hr))
        {
            d3d->Release();
            ::DestroyWindow(g_window);
            return 1;
        }
        g_presentation.BackBufferFormat = dm.Format;

        // now create the device
        hr = d3d->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL,
            g_window, D3DCREATE_SOFTWARE_VERTEXPROCESSING,
            &g_presentation, &g_device);
        d3d->Release();
        if (FAILED(hr))
        {
            ::DestroyWindow(g_window);
            return 1;
        }
    }

    // display the window
    ::ShowWindow(g_window, SW_SHOWDEFAULT);
    ::UpdateWindow(g_window);

    // pump messages
    {
        MSG msg;
        while (0 < ::GetMessage(&msg, g_window, 0, 0))
        {
            ::TranslateMessage(&msg);
            ::DispatchMessage(&msg);
        }
    }

    // clean up resources
    g_device->Release();
    ::DestroyWindow(g_window);

    return 0;
}
