//i686-w64-mingw32-g++ -m32  -Wno-deprecated-declarations -fpermissive -g -D__STDC_CONSTANT_MACROS -DCINTERFACE -Wall -O0 -ggdb shaders.cpp -o shaders.exe  -Wl,-Bstatic -lwinpthread -Wl,-Bdynamic -lole32 -loleaut32 -lshlwapi -luuid -lpsapi -lstrmiids -ladvapi32 -lshell32 -ld3d9 -ld3dx9 -ldxguid -lgdi32 -lavicap32 -lgdiplus


char shader[] =" \
    float4x4 ShaderMatrix;                                             \
struct VS_OUTPUT                                                        \
{                                                                       \
	float4 PositionOut : POSITION;                                  \
	float4 ColorOut : COLOR;                                        \
};                                                                      \
                                                                        \
//Vertex Shader                                                         \
VS_OUTPUT VS(float4 PositionIn : POSITION, float4 ColorIn : COLOR)      \
{                                                                       \
	VS_OUTPUT VertexOut;                                            \
	VertexOut.PositionOut = mul(PositionIn, ShaderMatrix);          \
	VertexOut.ColorOut = ColorIn;                                   \
	return VertexOut;                                               \
}                                                                       \
                                                                        \
//Pixel Shader                                                          \
float4 PS(float4 Color : COLOR) : COLOR                                 \
{                                                                       \
    return Color;                                                       \
}                                                                       \
                                                                        \
technique RenderScene                                                   \
{                                                                       \
    pass P0                                                             \
    {                                                                   \
        VertexShader = compile vs_1_1 VS();                             \
        PixelShader  = compile ps_1_1 PS();                             \
    }                                                                   \
}";

//Every windows application needs to include this
#include "windows.h"
#include <lmerr.h>
#include <stdio.h>
//Every Direct3D application this
#include "d3d9.h"
#include "d3dx9.h"
#include "d3dx9effect.h"


bool g_bContinue = true;
LPD3DXEFFECT g_lpEffect = NULL;
LPDIRECT3DVERTEXBUFFER9 g_lpVertexBuffer = NULL;
D3DXMATRIX g_ShaderMatrix;

//Definition of the Vertex Format including position and diffuse color
#define D3DFVF_COLOREDVERTEX (D3DFVF_XYZ | D3DFVF_DIFFUSE)

struct COLORED_VERTEX
{
	float x, y, z;	//Position
	DWORD color;	//Color
};

#define ERRMSGBUFFERSIZE 256
void displayError( DWORD dwErrorMsgId )
{
    DWORD ret;        // Temp space to hold a return value.
    HINSTANCE hInst;  // Instance handle for DLL.
    HLOCAL pBuffer;   // Buffer to hold the textual error description.
    
    if ( HRESULT_FACILITY(dwErrorMsgId) == FACILITY_MSMQ )
    { // MSMQ errors only (see winerror.h for facility info).
// Load the MSMQ library containing the error message strings.
	hInst = LoadLibrary( TEXT("MQUTIL.DLL") );
	if(hInst != 0)
	{ // hInst not NULL if the library was successfully loaded.
// Get the text string for a message definition
            ret = FormatMessage( 
                FORMAT_MESSAGE_ALLOCATE_BUFFER | // Function will handle memory allocation.
                FORMAT_MESSAGE_FROM_HMODULE | // Using a module's message table.
                FORMAT_MESSAGE_IGNORE_INSERTS, 
                hInst, // Handle to the DLL.
                dwErrorMsgId, // Message identifier.
                MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language.
                (LPTSTR)&pBuffer, // Buffer that will hold the text string.
                ERRMSGBUFFERSIZE, // Allocate at least this many chars for pBuffer.
                NULL // No insert values.
                );
	} // hInst not NULL if the library was successfully loaded.

    } // MSMQ errors only.

    else if ( dwErrorMsgId >= NERR_BASE && dwErrorMsgId <= MAX_NERR )
    { // Could be a network error.
// Load the library containing network messages.
	hInst = LoadLibrary( TEXT("NETMSG.DLL") );
	if(hInst != 0)
	{ // Not NULL if successfully loaded.
// Get a text string for the message definition.
            ret = FormatMessage(  
                FORMAT_MESSAGE_ALLOCATE_BUFFER | // The function will allocate memory for the message.
                FORMAT_MESSAGE_FROM_HMODULE | // Message definition is in a module.
                FORMAT_MESSAGE_IGNORE_INSERTS,  // No inserts used.
                hInst, // Handle to the module containing the definition.
                dwErrorMsgId, // Message identifier.
                MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language.
                (LPTSTR)&pBuffer, // Buffer to hold the text string.
                ERRMSGBUFFERSIZE, // Smallest size that will be allocated for pBuffer.
                NULL // No inserts.
                );
	} // Not NULL if successfully loaded.

    } // Could be a network error.
    else
    { // Unknown message source.
// Get the message string from the system.
	ret = FormatMessage(  
            FORMAT_MESSAGE_ALLOCATE_BUFFER | // The function will allocate space for pBuffer.
            FORMAT_MESSAGE_FROM_SYSTEM | // System wide message.
            FORMAT_MESSAGE_IGNORE_INSERTS, // No inserts.
            NULL, // Message is not in a module.
            dwErrorMsgId, // Message identifier.
            MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language.
            (LPTSTR)&pBuffer, // Buffer to hold the text string.
            ERRMSGBUFFERSIZE, // The function will allocate at least this much for pBuffer.
            NULL // No inserts.
            );
    }


// Display the string.
    
    if( ret )
    {
        fprintf(stderr, "\tERRORMESSAGE: %s\n", (LPTSTR)pBuffer );
    }
    else
    {
        fprintf(stderr, "\tERRORNUMBER: %d\n", dwErrorMsgId );
    }

    
// Free the buffer.
    LocalFree( pBuffer );
    
}


//Besides the main function, there must be a message processing function
LRESULT WINAPI MsgProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
	switch( msg )
	{
		case WM_DESTROY:
			PostQuitMessage( 0 );
			g_bContinue = false;
			return 0;
	}
	return DefWindowProc( hWnd, msg, wParam, lParam );
}

//The entry point of a windows application is the WinMain function
INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR, INT )
{
	//Create a window class.
	WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_CLASSDC, MsgProc, 0L, 0L, 
					GetModuleHandle(NULL), NULL, NULL, NULL, NULL,
					"Direct3D Window", NULL };

	//Register the window class.
	RegisterClassEx( &wc );

	//Create the application's window.
	HWND hWnd = CreateWindow( "Direct3D Window", "Direct3d Shaders", 
							WS_OVERLAPPEDWINDOW, 100, 100, 400, 400,
							GetDesktopWindow(), NULL, wc.hInstance, NULL );

	ShowWindow(hWnd,SW_SHOW);

	//Create the Direct3D Object
	LPDIRECT3D9 pD3D = NULL;
	if( NULL == (pD3D = Direct3DCreate9(D3D_SDK_VERSION)))
	  return E_FAIL;

	//Setup the device presentation parameters
	D3DPRESENT_PARAMETERS d3dpp; 
	ZeroMemory( &d3dpp, sizeof(d3dpp) );
	d3dpp.Windowed = TRUE;
	d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
	d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;

	//The final step is to use the IDirect3D9::CreateDevice method to create the Direct3D device, as illustrated in the
	//following code example.
	LPDIRECT3DDEVICE9 pd3dDevice = NULL;
	if( FAILED(IDirect3D9_CreateDevice(pD3D, D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd,
                                           D3DCREATE_HARDWARE_VERTEXPROCESSING,
                                           &d3dpp, &pd3dDevice ) ) )
	{
		MessageBox(hWnd, "No HAL HARDWARE_VERTEXPROCESSING! Sample will exit!", NULL, 0);
                IDirect3D9_Release(pD3D);
		pD3D = NULL;
		return E_FAIL;
	}

        printf("1\n");
	//set the vertex buffer size 4 vertices * vertex structure size
	UINT uiBufferSize = 4*sizeof(COLORED_VERTEX);

	//create the buffer
	if( FAILED( IDirect3DDevice9_CreateVertexBuffer(pd3dDevice, uiBufferSize,
			D3DUSAGE_WRITEONLY, D3DFVF_COLOREDVERTEX, D3DPOOL_DEFAULT, &g_lpVertexBuffer, NULL ) ) )
		return E_FAIL;
	
	COLORED_VERTEX* pVertices;
	//lock the buffer for writing
	if( FAILED( IDirect3DVertexBuffer9_Lock(g_lpVertexBuffer, 0, uiBufferSize, (void**)&pVertices, 0 ) ) )
		return E_FAIL;

	//write the vertices. Here a simple rectangle
	pVertices[0].x =  -1.0f; //left
	pVertices[0].y =  -1.0f; //bottom
	pVertices[0].z =   0.0f;
	pVertices[0].color =  0xffff0000; //red

	pVertices[1].x =  -1.0f; //left
	pVertices[1].y =   1.0f; //top
	pVertices[1].z =   0.0f;
	pVertices[1].color =  0xff0000ff; //blue

	pVertices[2].x =   1.0f; //right
	pVertices[2].y =  -1.0f; //bottom
	pVertices[2].z =   0.0f;
	pVertices[2].color =  0xff00ff00; //green

	pVertices[3].x =  1.0f; //right
	pVertices[3].y =  1.0f; //top 
	pVertices[3].z =  0.0f;
	pVertices[3].color =  0xffffffff; //white

	//unlock the buffer
        IDirect3DVertexBuffer9_Unlock(g_lpVertexBuffer);

	//set the Vertex Format
	IDirect3DDevice9_SetFVF(pd3dDevice, D3DFVF_COLOREDVERTEX );

	//transfer the buffer to the gpu
	IDirect3DDevice9_SetStreamSource(pd3dDevice, 0, g_lpVertexBuffer, 0, sizeof(COLORED_VERTEX) );

	//create an effect
        printf("===========\n%s\n=================\n", shader);
        LPD3DXBUFFER err = 0;
        // HRESULT hr = D3DXCreateEffect( pd3dDevice, shader, strlen(shader), NULL, 
        //                                         NULL, D3DXSHADER_ENABLE_BACKWARDS_COMPATIBILITY,
        //                                         NULL,
        //                                         &g_lpEffect,
        //                                         &err);
        
        HRESULT hr = D3DXCreateEffectFromFile( pd3dDevice, "Effect.fx", NULL, 
                                               NULL, D3DXSHADER_ENABLE_BACKWARDS_COMPATIBILITY,
                                               NULL,
                                               &g_lpEffect,
                                               &err);
        if (FAILED(hr))
        {
            printf("Cannot load Effect.fx: %p\n", err);

            if (err)
                MessageBox(NULL, (const char *)ID3DXBuffer_GetBufferPointer(err), "Critical Failure", MB_OK);
            displayError(hr);
            return E_FAIL;
        }

	D3DXMatrixIdentity(&g_ShaderMatrix);
	(g_lpEffect)->lpVtbl->SetMatrix( g_lpEffect, "ShaderMatrix", &g_ShaderMatrix );

	MSG msg; 
	while( g_bContinue )
	{
            //Clear render region with blue
            IDirect3DDevice9_Clear(pd3dDevice, 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0,0,255), 1.0f, 0 );

            //before rendering something, you have to call this
            IDirect3DDevice9_BeginScene(pd3dDevice);

            //rendering of scene objects happens here

            // //begin the effect
            // UINT uiPasses = 0;
            // (g_lpEffect)->lpVtbl->Begin(g_lpEffect, &uiPasses, 0);
            // printf("uiPasses: %d\n", uiPasses);
            // for (UINT uiPass = 0; uiPass < uiPasses; uiPass++)
            // {
            //     //render an effect pass
            //     (g_lpEffect)->lpVtbl->BeginPass(g_lpEffect, uiPass);
                
            //     //render the rectangle
            //     IDirect3DDevice9_DrawPrimitive(pd3dDevice, D3DPT_TRIANGLESTRIP, 0, 2 );

            //     (g_lpEffect)->lpVtbl->EndPass(g_lpEffect);
            // }
            // (g_lpEffect)->lpVtbl->End(g_lpEffect);

            //after the scene call
            IDirect3DDevice9_EndScene(pd3dDevice);

            //update screen = swap front and backbuffer
            IDirect3DDevice9_Present(pd3dDevice, NULL, NULL, NULL, NULL);

            // A window has to handle its messages.
            TranslateMessage( &msg );
            DispatchMessage( &msg );
            PeekMessage(&msg, 0, 0, 0, PM_REMOVE);
	}

	//Do not forget to clean up here
        IDirect3D9_Release(pd3dDevice);
	pd3dDevice = NULL;
        IDirect3D9_Release(pD3D);
	pD3D = NULL;     
	IDirect3D9_Release(g_lpEffect);
	g_lpEffect = NULL;
	IDirect3D9_Release(g_lpVertexBuffer);
	g_lpVertexBuffer = NULL;
	return 0;
}
