// d3d8test.cpp
// From 2D Guide for DirectX 8 Graphics
// Tutorial was written by Jay Watkins

#define WIN32_LEAN_AND_MEAN

#include <windows.h>
#include <windowsx.h>
#include <d3d8.h>
#include <dxerr8.h>


// The screen width and height constants
const UINT SCREEN_WIDTH = 640;
const UINT SCREEN_HEIGHT = 480;

// The flags for the vertice
#define POINT_FLAGS (D3DFVF_XYZRHW | D3DFVF_DIFFUSE)

// The new vertice
struct Point {
	float x, y, z, rhw;
	DWORD color;
} lineList[1000];

// The class for the DX 8 Graphics
class DXGraphics
{
public:
	// Sets all the Interfaces to NULL
	DXGraphics();
	// Releases the Interfaces
	~DXGraphics();

private:
	HWND               hMainWnd;        // The Main Handle to the Window
	LPDIRECT3D8        D3DObject;       // The Direct3d Object
    LPDIRECT3DDEVICE8  D3DDevice;       // The Direct3d Device
	D3DDISPLAYMODE     D3DDisplayMode;  // The Display Mode
	LPDIRECT3DVERTEXBUFFER8 D3DVertexBuffer;  // The Vertex Buffer

public:
	// Enters into Full Screen mode
	int createD3DDevice();
	// Initializes the Graphics
	int createD3DObject(HWND window);
	// Creates the list of lines
	int generateLines();

	// inserts 2 points into the point list (creating a line)
	void line(float x1, float y1, float x2, float y2, DWORD color, int &index);
	// Creates the Vertex Buffer
	int createD3DVertexBuffer();
	// Loads the Vertex Buffer with the point list and renders it
	int renderIt();
};


DXGraphics::DXGraphics()
{
	D3DObject = NULL;
	D3DDevice = NULL;
	D3DVertexBuffer = NULL;
}

DXGraphics::~DXGraphics()
{
	if (D3DVertexBuffer != NULL) {
		D3DVertexBuffer->Release();
		D3DVertexBuffer = NULL;
	}
	if (D3DDevice != NULL) {
		D3DDevice->Release();
		D3DDevice = NULL;
	}
	if (D3DObject != NULL) {
		D3DObject->Release();
		D3DObject = NULL;
	}
}

int DXGraphics::createD3DObject(HWND window)
{
	HRESULT hResult;
	hMainWnd = window;

	if ((D3DObject = Direct3DCreate8(D3D_SDK_VERSION)) == NULL) { return E_FAIL; }
	
	hResult = D3DObject->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &D3DDisplayMode);
	if (hResult != D3D_OK) {
		DXTrace(__FILE__, __LINE__, hResult, "Can't Get Adapter Mode", TRUE);
		return E_FAIL;
	}
	
	return S_OK;
}

int DXGraphics::createD3DDevice()
{
	/* Create the Direct3D Device for rendering */
	D3DPRESENT_PARAMETERS d3dpp;
	ZeroMemory(&d3dpp, sizeof(d3dpp));

	d3dpp.Windowed = FALSE;
	d3dpp.hDeviceWindow = hMainWnd;
	d3dpp.SwapEffect = D3DSWAPEFFECT_FLIP;
	d3dpp.BackBufferCount = 2;
	d3dpp.BackBufferWidth = SCREEN_WIDTH;
	d3dpp.BackBufferHeight = SCREEN_HEIGHT;
	d3dpp.BackBufferFormat = D3DDisplayMode.Format;
	d3dpp.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;
	d3dpp.FullScreen_PresentationInterval = D3DPRESENT_INTERVAL_ONE;
		
	// A front buffer and 2 back buffers will be created as well
	D3DObject->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hMainWnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &D3DDevice);

	// Configure for 2d operations
	D3DDevice->SetRenderState(D3DRS_ZENABLE, FALSE);
	D3DDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
	D3DDevice->SetRenderState(D3DRS_LIGHTING, FALSE);
	
	return S_OK;
}

int DXGraphics::createD3DVertexBuffer()
{
	if (FAILED(D3DDevice->CreateVertexBuffer( 1000*sizeof(Point), D3DUSAGE_DONOTCLIP | D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, POINT_FLAGS, D3DPOOL_DEFAULT, &D3DVertexBuffer)) )
	{ return E_FAIL; }

	return S_OK;
}

void DXGraphics::line(float x1, float y1, float x2, float y2, DWORD color, int &index)
{
	// insert the line into the buffer
	lineList[index].x = x1;
	lineList[index].y = y1;
	lineList[index].z = 0.5f;
	lineList[index].rhw = 1.0f;
	lineList[index].color = color;
	
	lineList[index+1].x = x2,
	lineList[index+1].y = y2;
	lineList[index+1].z = 0.5f;
	lineList[index+1].rhw = 1.0f;
	lineList[index+1].color = color;

	index += 2;	
}


int DXGraphics::renderIt()
{
	VOID *pBuffer = NULL;

	// Lock the Vertex Buffer to get the pointer
	if (FAILED (D3DVertexBuffer->Lock(0, sizeof(lineList), (BYTE **)&pBuffer, D3DLOCK_DISCARD | D3DLOCK_NOSYSLOCK) ) )
	{ return E_FAIL; }

	// fill the buffer with the vertices
	memcpy(pBuffer, lineList, sizeof(lineList)); 

	// Unlock the buffer
	if ( FAILED ( D3DVertexBuffer->Unlock() ) ) { return E_FAIL; }
	
    if ( SUCCEEDED ( D3DDevice->BeginScene() ) )
	{
		D3DDevice->SetStreamSource(0, D3DVertexBuffer, sizeof(Point));
		D3DDevice->SetVertexShader( POINT_FLAGS );
		D3DDevice->DrawPrimitive( D3DPT_LINELIST, 0, 500 );

		if ( FAILED( D3DDevice->EndScene() ) ) { return E_FAIL; }
		D3DDevice->Present(NULL, NULL, NULL, NULL);
	}    
	else { return E_FAIL; }

	return S_OK;
}

int DXGraphics::generateLines() 
{
	for (int index = 0; index < 1000;) 
	{
		 // generate a color
		 DWORD color = D3DCOLOR_XRGB(0, 0, rand()%256);
		 // increments for us
	     line(rand()%SCREEN_WIDTH, rand()%SCREEN_HEIGHT, rand()%SCREEN_WIDTH, rand()%SCREEN_HEIGHT, color, index);
	}
	
	return S_OK;
}

LRESULT CALLBACK WinProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
	// The Main Message Handler of the System
	HDC hdc;			// Handler of Graphics Context
	PAINTSTRUCT ps;		// Holds Paint Information
	
	switch(Msg)
	{
	case WM_CREATE:
		return 0;
	    break;
	case WM_PAINT:
		hdc = BeginPaint(hWnd, &ps);
		EndPaint(hWnd, &ps);
		return 0;
	    break;
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	    break;
	case WM_KEYDOWN:
		if (wParam == VK_ESCAPE)
			PostQuitMessage(0);
		return 0;
		break;
	default: break;
	}

	return (DefWindowProc(hWnd, Msg, wParam, lParam));
} 

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
	DXGraphics DXTest;

	WNDCLASSEX window;  	// Holds Window Class Information
	HWND hwnd;				// Holds Window
	MSG msg;				// This is a Generic Message
	    
	window.cbSize           = sizeof(WNDCLASSEX);
    window.style			= CS_DBLCLKS | CS_OWNDC | 
                              CS_HREDRAW | CS_VREDRAW;
    window.lpfnWndProc	    = WinProc;
	window.cbClsExtra		= 0;
	window.cbWndExtra		= 0;
	window.hInstance		= hInstance;
	window.hIcon			= LoadIcon(NULL, IDI_APPLICATION);
	window.hCursor			= LoadCursor(NULL, IDC_ARROW); 
	window.hbrBackground	= (HBRUSH)GetStockObject(BLACK_BRUSH);
	window.lpszMenuName		= NULL;
	window.lpszClassName	= "DirectXWindow";
	window.hIconSm			= LoadIcon(NULL, IDI_APPLICATION);
	
	
	// register the window class
	if (!RegisterClassEx(&window))
		return 0;
	
	// create the window
	if (!(hwnd = CreateWindowEx(NULL,		        // Top-Most Window
		  "DirectXWindow",							// Window Class
		  "DirectX 8 Application",					// Caption
		  WS_EX_TOPMOST | WS_POPUP | WS_VISIBLE,	
		  0,0,							      		// x,y								
		  SCREEN_WIDTH,
		  SCREEN_HEIGHT, 
		  NULL,										// No Parent Window
		  NULL,										// No Menu
		  hInstance,								// Instance
		  NULL)))									// Creation Parms
	{
	       	return 0;
	}

	if (DXTest.createD3DObject(hwnd) == E_FAIL) { return 0; }
	if (DXTest.createD3DDevice() == E_FAIL) { return 0; }
	if (DXTest.createD3DVertexBuffer() == E_FAIL) { return 0; }

	while (1)
	{
	
		if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
		{
			if (msg.message == WM_QUIT)
				break;
		    // Get any accelerator keys
			TranslateMessage(&msg);
			// Do what the message was
			DispatchMessage(&msg);
		}
		DXTest.generateLines();
		DXTest.renderIt();

	} // end while


// return to Windows
return(msg.wParam);
    
}
