/*
	Ripatti Artem (c) 2011
*/

#define _CRT_SECURE_NO_WARNINGS

#include "ge_d3d9c.h"
#include "log.h"
#include <stdio.h>
#include <stdlib.h>

namespace ge
{
	GraphicEngineD3D9c::GraphicEngineD3D9c(HWND hWnd, int scr_x, int scr_y, bool windowed)
	{
		g_D3D = 0;
		g_Device = 0;
		for (int a=0; a<MAX_TEXTURE; a++)
			g_Texture[a] = 0;
		g_Font = 0;
		g_Buffer = 0;
		OLD_RT = 0;
		RT = 0;

		Message("GE", "GE init...");
		if (NULL == (g_D3D = Direct3DCreate9(D3D_SDK_VERSION)))
			Error("GE", "Cannot create D3D");
		Message("GE", "D3D is created");

		D3DPRESENT_PARAMETERS d3dpp; 
		ZeroMemory (&d3dpp, sizeof(d3dpp));
		d3dpp.Windowed = (windowed ? 1 : 0);
		d3dpp.BackBufferCount  = 1;
		d3dpp.hDeviceWindow    = hWnd;
		d3dpp.BackBufferWidth  = scr_x;
		d3dpp.BackBufferHeight = scr_y;
		d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8;
		d3dpp.SwapEffect = D3DSWAPEFFECT_FLIP;
		d3dpp.EnableAutoDepthStencil = FALSE;
		//d3dpp.AutoDepthStencilFormat = D3DFMT_D24X8;
		d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
		d3dPP = d3dpp;

		if(FAILED(g_D3D->CreateDevice (D3DADAPTER_DEFAULT,
										D3DDEVTYPE_HAL,
										hWnd,
										D3DCREATE_HARDWARE_VERTEXPROCESSING,
										&d3dpp,
										&g_Device)))
		{
			if (FAILED(g_D3D->CreateDevice (D3DADAPTER_DEFAULT,
										D3DDEVTYPE_HAL,
										hWnd,
										D3DCREATE_SOFTWARE_VERTEXPROCESSING,
										&d3dpp,
										&g_Device)))
			{
				if (FAILED(g_D3D->CreateDevice (D3DADAPTER_DEFAULT,
										D3DDEVTYPE_REF,
										hWnd,
										D3DCREATE_SOFTWARE_VERTEXPROCESSING,
										&d3dpp,
										&g_Device)))
					Error("GE", "Cannot create Device");
			}
		}
		Message("GE", "Device is created");

		g_Device->SetRenderState(D3DRS_CULLMODE,				D3DCULL_NONE);
		g_Device->SetRenderState(D3DRS_SHADEMODE,				D3DSHADE_GOURAUD);
		g_Device->SetRenderState(D3DRS_ZENABLE,					FALSE);
		g_Device->SetRenderState(D3DRS_ALPHABLENDENABLE,		TRUE);
		g_Device->SetRenderState(D3DRS_SRCBLEND,				D3DBLEND_SRCALPHA);
		g_Device->SetRenderState(D3DRS_DESTBLEND,				D3DBLEND_INVSRCALPHA);
		g_Device->SetRenderState(D3DRS_DIFFUSEMATERIALSOURCE,	D3DMCS_COLOR1);
		g_Device->SetRenderState(D3DRS_COLORVERTEX,				TRUE);

		g_Device->SetTextureStageState( 0, D3DTSS_COLOROP,   D3DTOP_MODULATE );
		g_Device->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE );
		g_Device->SetTextureStageState( 0, D3DTSS_COLORARG2, D3DTA_DIFFUSE );
		
		g_Device->SetTextureStageState( 0, D3DTSS_ALPHAOP,   D3DTOP_MODULATE );
		g_Device->SetTextureStageState( 0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE );
		g_Device->SetTextureStageState( 0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE );

		//MulColor = D3DCOLOR_ARGB(255, 255, 255, 255);

		g_Device->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_POINT );
		g_Device->SetSamplerState( 0, D3DSAMP_MAGFILTER, D3DTEXF_POINT );

		D3DXCreateTexture(g_Device,
							512, 256,
							1,
							D3DUSAGE_RENDERTARGET,
							D3DFMT_A8R8G8B8,
							D3DPOOL_DEFAULT,
							&g_Texture[0]);
		ScTexX[0] = 1.0f;
		ScTexY[0] = 1.0f;

		//for (int a = 0; a < 4; a ++)
		//{
			/*

			D3DXCreateTexture(g_Device,
							512, 512,
							1,
							D3DUSAGE_RENDERTARGET,
							D3DFMT_A8R8G8B8,
							D3DPOOL_DEFAULT,
							&g_Texture[1]);
			ScTexX[1] = 1.0f;
			ScTexY[1] = 0.25f;

			D3DXCreateTexture(g_Device,
							512, 512,
							1,
							D3DUSAGE_RENDERTARGET,
							D3DFMT_A8R8G8B8,
							D3DPOOL_DEFAULT,
							&g_Texture[2]);
			ScTexX[2] = 0.75f;
			ScTexY[2] = 0.75f;*/
		//}

		Message("GE", "Options was changed");

		// Øðèôò
		if( FAILED( D3DXCreateFont( g_Device, 14, 0, FW_NORMAL, 1, FALSE, 1, 
							OUT_DEFAULT_PRECIS, ANTIALIASED_QUALITY, DEFAULT_PITCH | FF_MODERN, 
							TEXT(/*"Comic Sans MS"*/"Courier New"), &g_Font ) ) )
			Error("GE", "Cannot create Fonts");
		Message("GE", "Fonts are created");

		// Êó÷à
		g_Device->CreateVertexBuffer( 128000*sizeof(VERTEX2D),
			0, 0, D3DPOOL_SYSTEMMEM, &g_Buffer, NULL );
		Message("GE", "Vertex buffer is created");

		g_Device->SetStreamSource( 0, g_Buffer, 0, sizeof(VERTEX2D) );
		g_Device->SetFVF( D3DFVF_XYZRHW|D3DFVF_TEX1|D3DFVF_DIFFUSE );
		Message("GE", "GE was loaded");
	}

	GraphicEngineD3D9c::~GraphicEngineD3D9c()
	{
		Message("GE", "GE killing...");
		if (g_Buffer)
			g_Buffer->Release();

		if (g_Font)
			g_Font->Release(); 

		for (int a = 0; a < MAX_TEXTURE; a ++)
			if (g_Texture[a])
				g_Texture[a]->Release();

		if (g_Device)
			g_Device->Release();

		if (g_D3D)
			g_D3D->Release();

		Message("GE", "GE was killed");
	}

	void GraphicEngineD3D9c::load_texture(int N, const char * File)
	{
		if (FAILED(D3DXCreateTextureFromFileEx(
			g_Device,
			File,
			0,
			0,
			0,
			0,
			D3DFMT_UNKNOWN,//D3DFMT_R8G8B8,
			D3DPOOL_MANAGED,
			D3DX_DEFAULT,
			D3DX_DEFAULT,
			NULL,
			NULL,
			NULL,
			&g_Texture[N])))
			Error("GE", "Cannot load texture");
	}

	void GraphicEngineD3D9c::load_texture(int N, const char * File, const char * Alpha)
	{
		BYTE c[3];
		DWORD x[100];
		int StX=1, StY=1;

		D3DXIMAGE_INFO Info;

		FILE * Fle;

		D3DXGetImageInfoFromFile(File, &Info);

		if (Info.ImageFileFormat != D3DXIFF_BMP)
			Error("GE", "Do not know this format");

		while (1)
		{
			if (StX >= (int)Info.Width) break;
			StX*=2;
		}
		while (1)
		{
			if (StY >= (int)Info.Height) break;
			StY*=2;
		}
		ScTexX[N] = (float)(Info.Width)/(float)(StX);
		ScTexY[N] = (float)(Info.Height)/(float)(StY);
		for (int a = 0; a < StX; a ++)
			for (int b = 0; b < StY; b ++)
			{
				BitR[a][b] = 0;
				BitG[a][b] = 0;
				BitB[a][b] = 0;
				BitA[a][b] = 0;
			}
		D3DXCreateTexture(g_Device,
						//Info.Width, Info.Height,
						StX, StY,
						1,
						0,
						D3DFMT_A8R8G8B8,
						D3DPOOL_MANAGED,
						&g_Texture[N]);

		D3DLOCKED_RECT d3dlr;
		g_Texture[N]->LockRect(0, &d3dlr, 0, 0);
		DWORD* pSrc = (DWORD*)d3dlr.pBits;

		if (Info.ImageFileFormat == D3DXIFF_BMP)
		{
			Fle = fopen(File, "rb");
			fread(&x, 54, 1, Fle);
			long del3;
			for (int y = 0; y < (int)Info.Height; y ++)
			{
				del3 = 0;
				for (int x = 0; x < (int)Info.Width; x ++)
				{
					fread(&c, 1, 3, Fle);
					del3++;
					//Bitmap[x][y] = rgb(c[2], c[1], c[0]);
					BitR[x][y+StY-Info.Height] = c[2];
					BitG[x][y+StY-Info.Height] = c[1];
					BitB[x][y+StY-Info.Height] = c[0];
				}
				fread(&c, 1, del3%4, Fle);
			}
			fclose(Fle);
			if (strcmp(Alpha,"Auto") == 0)
			{
				for (int y = 0; y < (int)Info.Height; y ++)
					for (int x = 0; x < (int)Info.Width; x ++)
						if (BitR[x][y+StY-Info.Height] == 0 &&
							BitG[x][y+StY-Info.Height] == 0 &&
							BitB[x][y+StY-Info.Height] == 0)
							BitA[x][y+StY-Info.Height] = 0;
						else BitA[x][y+StY-Info.Height] = 255;
			}
			else if (strcmp(Alpha, "Null") != 0)
			{
				Fle = fopen(Alpha, "rb");
				fread(&x, 54, 1, Fle);
				for (int y = 0; y < (int)Info.Height; y ++)
				{
					del3 = 0;
					for (int x = 0; x < (int)Info.Width; x ++)
					{
						fread(&c, 1, 3, Fle);
						del3++;
						BitA[x][y+StY-Info.Height] = c[0];
					}
					fread(&c, 1, del3%4, Fle);
				}
				fclose(Fle);
			}
			for (int y = StY-1; y >= 0; y --)
				for (int x = 0; x < StX; x ++)
					if (strcmp(Alpha, "Null") != 0)
						*pSrc++ = argb(BitA[x][y], BitR[x][y], BitG[x][y], BitB[x][y]);
					else
						*pSrc++ = rgb(BitR[x][y], BitG[x][y], BitB[x][y]);
			g_Texture[N]->UnlockRect(0);
		}
	}

	//void GraphicEngineD3D9c::Reset()
	//{
	//	g_Device->Reset(&d3dPP);
	//}

	void GraphicEngineD3D9c::set_texture(int T)
	{
		if (T==cur_tex) return;
		cur_tex = T;
		ScaleX = ScTexX[T];
		ScaleY = ScTexY[T];
	}

	PICTURE GraphicEngineD3D9c::pict_to_picture(PICT & Pict)
	{
		PICTURE re;

		re.v1.v.x = Pict.v[0].x-0.5f; re.v1.v.y = Pict.v[0].y-0.5f; re.v1.v.z = 0.5f;
		re.v1.rhw = 1.0f; re.v1.tx = Pict.v[0].tx; re.v1.ty = Pict.v[0].ty;
		re.v1.clr = Pict.v[0].clr;

		re.v2.v.x = Pict.v[1].x-0.5f; re.v2.v.y = Pict.v[1].y-0.5f; re.v2.v.z = 0.5f;
		re.v2.rhw = 1.0f; re.v2.tx = Pict.v[1].tx; re.v2.ty = Pict.v[1].ty;
		re.v2.clr = Pict.v[1].clr;

		re.v3.v.x = Pict.v[2].x-0.5f; re.v3.v.y = Pict.v[2].y-0.5f; re.v3.v.z = 0.5f;
		re.v3.rhw = 1.0f; re.v3.tx = Pict.v[2].tx; re.v3.ty = Pict.v[2].ty;
		re.v3.clr = Pict.v[2].clr;

		re.v4.v.x = Pict.v[3].x-0.5f; re.v4.v.y = Pict.v[3].y-0.5f; re.v4.v.z = 0.5f;
		re.v4.rhw = 1.0f; re.v4.tx = Pict.v[3].tx; re.v4.ty = Pict.v[3].ty;
		re.v4.clr = Pict.v[3].clr;

		return re;
	}

	void GraphicEngineD3D9c::draw_pict(PICT & pict)
	{
		PICTURE pct = pict_to_picture(pict);

		verts[v_sz*4+0] = pct.v1;
		verts[v_sz*4+0].tx *= ScaleX;
		verts[v_sz*4+0].ty *= ScaleY;
		verts[v_sz*4+1] = pct.v2;
		verts[v_sz*4+1].tx *= ScaleX;
		verts[v_sz*4+1].ty *= ScaleY;
		verts[v_sz*4+2] = pct.v3;
		verts[v_sz*4+2].tx *= ScaleX;
		verts[v_sz*4+2].ty *= ScaleY;
		verts[v_sz*4+3] = pct.v4;
		verts[v_sz*4+3].tx *= ScaleX;
		verts[v_sz*4+3].ty *= ScaleY;
		texN[v_sz] = cur_tex;
		v_sz++;

		if (v_sz >= 30000) render_stream();
	}

	void GraphicEngineD3D9c::render_stream()
	{
		VOID* pM;

		g_Buffer->Lock( 0, 4 * sizeof(VERTEX2D) * v_sz, (void**)&pM, 0 );
		memcpy( pM, verts, 4 * sizeof(VERTEX2D) * v_sz );
		g_Buffer->Unlock();

		cur_tris += v_sz*2;
		int tmp=-2;
		for (int a=0; a<v_sz; a++)
		{
			if (texN[a]!=tmp)
			{
				g_Device->SetTexture(0, g_Texture[texN[a]]);
				tmp = texN[a];
			}
			g_Device->DrawPrimitive( D3DPT_TRIANGLESTRIP, a*4, 2 );
		}
		
		v_sz=0;
	}

	int GraphicEngineD3D9c::get_tris()
	{
		return tris;
	}

	void GraphicEngineD3D9c::clear_scene(float R, float G, float B)
	{
		if (NULL == g_Device)
			Error("GE", "Not Device");

		g_Device->Clear (0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB((int)(R*255),(int)(G*255),(int)(B*255)), 1.0f, 0);
	}

	void GraphicEngineD3D9c::begin_scene()
	{
		if (NULL == g_Device)
			Error("GE", "Not Device");
		g_Device->BeginScene();
		v_sz=0;
		tris=cur_tris;
		cur_tris=0;
	}

	void GraphicEngineD3D9c::render_scene()
	{
		if (NULL == g_Device)
			Error("GE", "Not Device");

		render_stream();
		g_Device->SetTexture(0, NULL);

		g_Device->EndScene();

		g_Device->Present (NULL, NULL, NULL, NULL);
	}

	void GraphicEngineD3D9c::write(float x, float y, const char* ch, float R, float G, float B, float A)
	{
		render_stream();

		RECT rc;

		SetRect(&rc, (int)x-250, (int)y, (int)x+250, (int)y+100 );
		if (FAILED(g_Font->DrawText( NULL, ch, -1, &rc, DT_NOCLIP|DT_CENTER,
			D3DCOLOR_ARGB( (int)(A*255), (int)(R*255),(int)(G*255),(int)(B*255) ))))
			Error("GE", "DrawText error");
	}

	void GraphicEngineD3D9c::render_to_texture(int T)
	{
		render_stream();
		if (T == -1)
			g_Device->SetRenderTarget(0, OLD_RT);
		else
		{
			g_Device->GetRenderTarget(0, &OLD_RT);
			g_Texture[T]->GetSurfaceLevel(0, &RT);
			g_Device->SetRenderTarget(0, RT);
		}
	}
}