/*
	BinMaker program
	Copyright 2007 Jonathan Wilson
	Portions lifted from BinOpener and TerrainTextureCompiler copyright 2007 booto (temptemp91 at hotmail dot com)

	This file is part of binmaker.

	This program is free software; you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation; either version 2 of the License, or
	(at your option) any later version.

	binmaker is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program; if not, write to the Free Software
	Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

*/
#include <stdio.h>
#include "manifestitem.h"
#include "terraintextureatlas.h"
#include "common.h"
#include "bigaxes.h"
HRESULT hr;
struct VertexType
{
	FLOAT x;
	FLOAT y;
	FLOAT z;
	FLOAT rhw;
	FLOAT tu;
	FLOAT tv;
};
const DWORD VertexTypeFVF = (D3DFVF_XYZRHW |  D3DFVF_TEX1 );
TerrainTextureAtlas::TerrainTextureAtlas(TiXmlNode *node)
{
	std::vector<TerrainTexture> tta;
	std::vector<TerrainTextureEntry> trailer;
	DWORD length;
	HKEY hKey;
	const char *id = node->ToElement()->Attribute("id");
	char *stream = new char[strlen(id)+21];
	sprintf(stream,"TerrainTextureAtlas:%s",id);
	StreamName = stream;
	SourceName = _strdup("DATA:map.xml");
	_strlwr((char *)id);
	Header.filenamehash1 = 0x5f74536d;
	Header.filenamehash2 = hash_function(id,strlen(id));
	Header.filenamehash3 = 0x7b1a8a04;
	Header.filenamehash4 = Header.filenamehash2;
	RegOpenKeyEx(HKEY_LOCAL_MACHINE,"Software\\Electronic Arts\\Electronic Arts\\Command and Conquer 3",0,KEY_READ,&hKey);
	RegQueryValueEx(hKey,"InstallPath",NULL,NULL,NULL,&length);
	BYTE *c = new BYTE[length+20];
	RegQueryValueEx(hKey,"InstallPath",NULL,NULL,c,&length);
	c[length] = 0x00;
	RegCloseKey(hKey);
	char *art_big = (char *)c;
	strcat(art_big,"Core\\1.0\\Art.big");
	BIGFile *bf = BigOpen(art_big);
	BIGFileEntry *bfe = NULL;
	unsigned int borderwidth = 16;
	unsigned int atlas_size = 0;
	atlas_size = 2048;
	for (TiXmlNode *child = node->FirstChild(); child; child = child->NextSibling())
	{
		if (child->Type() != TiXmlNode::ELEMENT)
		{
			continue;
		}
		if (child->ValueStr() == "Tile")
		{
			TerrainTexture new_texture;
			ZeroMemory(&new_texture, sizeof(new_texture));
			const char *path = child->ToElement()->Attribute("BaseTexture");
			if (!strncmp(path,"ART:",4))
			{
				if(bf)
				{
					int len = strlen(strchr(path,':') + 1) + 5;
					char *big_path = new char[len];
					sprintf(big_path,"art\\%s",strchr(path,':') + 1);
					while (strchr(big_path,'/'))
					{
						strchr(big_path,'/')[0] = '\\';
					}
					bfe = BigLoadEntryByName(bf, big_path);
				}
			}
			if (bfe)
			{
				hr = D3DXCreateTextureFromFileInMemoryEx(d3ddev, bfe->content, bfe->un_size, D3DX_DEFAULT,
											 D3DX_DEFAULT, D3DX_FROM_FILE, D3DUSAGE_DYNAMIC, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT,
											 D3DX_DEFAULT, D3DX_DEFAULT, 0, &new_texture.d_inf, NULL, &new_texture.diffuse);
			}
			if (SUCCEEDED(hr))
			{
				LPDIRECT3DSURFACE9 this_surf = NULL;
				D3DLOCKED_RECT dr;
				A8R8G8B8_s *s;
				D3DSURFACE_DESC d3dsd;
				new_texture.diffuse->GetSurfaceLevel(0, &this_surf);
				this_surf->GetDesc(&d3dsd);
				this_surf->LockRect(&dr, NULL, 0);
				s = (A8R8G8B8_s *)dr.pBits;
				for (unsigned int j = 0;j < d3dsd.Height;j++)
				{
					for (unsigned int i = 0; i < d3dsd.Width; i++)
					{
						if (s[i].a == 0xff)
						{
							s[i].a = 0;
						}
					}
					s=(A8R8G8B8_s *)((unsigned char *)s + dr.Pitch);
				}
				this_surf->UnlockRect();
				this_surf->Release();
			}
			path = child->ToElement()->Attribute("NormalTexture");
			if (!strncmp(path,"ART:",4))
			{
				if(bf)
				{
					int len = strlen(strchr(path,':') + 1) + 5;
					char *big_path = new char[len];
					sprintf(big_path,"art\\%s",strchr(path,':') + 1);
					while (strchr(big_path,'/'))
					{
						strchr(big_path,'/')[0] = '\\';
					}
					bfe = BigLoadEntryByName(bf, big_path);
				}
			}
			if (bfe)
			{
				hr = D3DXCreateTextureFromFileInMemoryEx(d3ddev, bfe->content, bfe->un_size, D3DX_DEFAULT,
											 D3DX_DEFAULT, D3DX_FROM_FILE, D3DUSAGE_DYNAMIC, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT,
											 D3DX_DEFAULT, D3DX_DEFAULT, 0, &new_texture.n_inf, NULL, &new_texture.normal);
			}
			if (child->ToElement()->Attribute("TextureID", &new_texture.id) &&
					new_texture.normal &&
					new_texture.diffuse)
			{
				tta.push_back(new_texture);
			}
			else
			{
				if (new_texture.diffuse)
				{
					new_texture.diffuse->Release();
					new_texture.diffuse = NULL;
				}
				if (new_texture.normal)
				{
					new_texture.normal->Release();
					new_texture.normal = NULL;
				}
			}
		}
	}
	if(bf)
	{
		BigClose(bf);
	}
	int n_rows = (tta.size()+6)/7;
	unsigned int atlas_height = 2;
	while(atlas_height<n_rows*(borderwidth*2+256))
	{
		atlas_height *= 2;
	}
	LPDIRECT3DVERTEXBUFFER9 vertbuff = NULL;
	LPD3DXRENDERTOSURFACE diffuse_render = NULL;
	LPDIRECT3DSURFACE9 diffuse_render_surface = NULL;
	LPDIRECT3DTEXTURE9 diffuse_dxt5_texture = NULL;
	LPD3DXRENDERTOSURFACE normal_render = NULL;
	LPDIRECT3DSURFACE9 normal_render_surface = NULL;
	LPDIRECT3DTEXTURE9 normal_x1r5g5b5_texture = NULL;
	hr = D3DXCreateTexture( d3ddev, atlas_size, atlas_height,
							5, D3DUSAGE_DYNAMIC, D3DFMT_DXT5, D3DPOOL_DEFAULT,
							&diffuse_dxt5_texture);
	hr = D3DXCreateTexture( d3ddev, atlas_size, atlas_height,
							5, D3DUSAGE_DYNAMIC, D3DFMT_X1R5G5B5, D3DPOOL_DEFAULT,
							&normal_x1r5g5b5_texture);
	d3ddev->CreateOffscreenPlainSurface(atlas_size, atlas_height, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &diffuse_render_surface, NULL);
	d3ddev->CreateOffscreenPlainSurface(atlas_size, atlas_height, D3DFMT_X8R8G8B8, D3DPOOL_DEFAULT, &normal_render_surface, NULL);
	hr = D3DXCreateRenderToSurface(d3ddev, atlas_size, atlas_height, D3DFMT_A8R8G8B8, TRUE,
								   D3DFMT_D16,  &diffuse_render);
	hr = D3DXCreateRenderToSurface(d3ddev, atlas_size, atlas_height, D3DFMT_X8R8G8B8, TRUE,
								   D3DFMT_D16,  &normal_render);
	hr = d3ddev->CreateVertexBuffer(4 * sizeof(VertexType), 0, VertexTypeFVF, D3DPOOL_DEFAULT, &vertbuff, NULL);
	POINT dst;
	unsigned int max_height;
	diffuse_render->BeginScene(diffuse_render_surface, NULL);
	d3ddev->Clear(0, NULL, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER, D3DCOLOR_COLORVALUE(0.0f, 0.0f, 0.0f, 0.0f), 1.0f, 0);
	dst.x=dst.y=0;
	max_height=0;
	for (unsigned int i = 0;i < tta.size();i++)
	{
		if (tta[i].d_inf.Width > 0xffff || tta[i].d_inf.Height > 0xffff)
		{
			continue;
		}
		if (tta[i].d_inf.Width == 0 || tta[i].d_inf.Height == 0)
		{
			continue;
		}
		if (dst.x + borderwidth + borderwidth + tta[i].d_inf.Width >= atlas_size)
		{
			if (dst.y + tta[i].d_inf.Height + borderwidth + borderwidth < atlas_height)
			{
				dst.y = dst.y + max_height;
				dst.x = 0;
				max_height = tta[i].d_inf.Height + borderwidth + borderwidth;
			}
			else
			{
				continue;
			}
		}
		else if (tta[i].d_inf.Height + borderwidth + borderwidth > max_height)
		{
			max_height = tta[i].d_inf.Height + borderwidth + borderwidth;
		}
		VertexType vertices[] =
			{
				{
					(float)((float)dst.x),								   (float)((float)dst.y),									   0.5f, 1.0f, (float)(-((float)borderwidth)/tta[i].d_inf.Width),	  (float)(-((float)borderwidth)/tta[i].d_inf.Height)
				},
				{(float)((float)dst.x + borderwidth * 2 + tta[i].d_inf.Width), (float)((float)dst.y),									   0.5f, 1.0f, (float)(1.0 + ((float)borderwidth)/tta[i].d_inf.Width), (float)(-((float)borderwidth)/tta[i].d_inf.Height)},
				{(float)((float)dst.x + borderwidth * 2 + tta[i].d_inf.Width), (float)((float)dst.y + borderwidth * 2 + tta[i].d_inf.Height), 0.5f, 1.0f, (float)(1.0 + ((float)borderwidth)/tta[i].d_inf.Width),   (float)(1.0 + ((float)borderwidth)/tta[i].d_inf.Height)},
				{(float)((float)dst.x),									  (float)((float)dst.y + borderwidth * 2 + tta[i].d_inf.Height), 0.5f, 1.0f, (float)(-((float)borderwidth)/tta[i].d_inf.Width),	  (float)(1.0 + ((float)borderwidth)/tta[i].d_inf.Height)},
			};
		const int num_verts = sizeof(vertices)/sizeof(*vertices);
		void *p_vb=NULL;
		hr = vertbuff->Lock(0, sizeof(vertices), &p_vb, 0);
		memcpy(p_vb, vertices, sizeof(vertices));
		vertbuff->Unlock();
		hr = d3ddev->SetFVF(VertexTypeFVF);
		d3ddev->SetStreamSource(0, vertbuff, 0, sizeof(VertexType));
		d3ddev->SetTexture(0, tta[i].diffuse);
		d3ddev->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_WRAP);
		d3ddev->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_WRAP);
		d3ddev->DrawPrimitive(D3DPT_TRIANGLEFAN, 0, num_verts-2);
		TerrainTextureEntry new_tte;
		new_tte.id = tta[i].id;
		new_tte.x1 = (unsigned short)(dst.x + borderwidth);
		new_tte.y1 = (unsigned short)(dst.y + borderwidth);
		new_tte.x2 = (unsigned short)(new_tte.x1 + tta[i].d_inf.Width);
		new_tte.y2 = (unsigned short)(new_tte.y1 + tta[i].d_inf.Height);
		trailer.push_back(new_tte);
		dst.x += tta[i].d_inf.Width + borderwidth * 2;
	}
	diffuse_render->EndScene(0);
	normal_render->BeginScene(normal_render_surface, NULL);
	d3ddev->Clear(0, NULL, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER, D3DCOLOR_COLORVALUE(0.0f, 0.0f, 0.0f, 0.0f), 1.0f, 0);
	dst.x=dst.y=0;
	max_height=0;
	for (unsigned int i = 0;i < tta.size();i++)
	{
		if (tta[i].n_inf.Width > 0xffff || tta[i].n_inf.Height > 0xffff)
		{
			continue;
		}
		if (tta[i].d_inf.Width == 0 || tta[i].d_inf.Height == 0)
		{
			continue;
		}
		if (dst.x + borderwidth + borderwidth + tta[i].n_inf.Width >= atlas_size)
		{
			if (dst.y + tta[i].n_inf.Height + borderwidth + borderwidth < atlas_height)
			{
				dst.y = dst.y + max_height;
				dst.x = 0;
				max_height = tta[i].n_inf.Height + borderwidth + borderwidth;
			}
			else
			{
				continue;
			}
		}
		else if (tta[i].n_inf.Height + borderwidth + borderwidth > max_height)
		{
			max_height = tta[i].n_inf.Height + borderwidth + borderwidth;
		}
		VertexType vertices[] =
			{
				{
					(float)((float)dst.x),								   (float)((float)dst.y),									   0.5f, 1.0f, (float)(-((float)borderwidth)/tta[i].d_inf.Width),	  (float)(-((float)borderwidth)/tta[i].d_inf.Height)
				},
				{(float)((float)dst.x + borderwidth * 2 + tta[i].d_inf.Width), (float)((float)dst.y),									   0.5f, 1.0f, (float)(1.0 + ((float)borderwidth)/tta[i].d_inf.Width), (float)(-((float)borderwidth)/tta[i].d_inf.Height)},
				{(float)((float)dst.x + borderwidth * 2 + tta[i].d_inf.Width), (float)((float)dst.y + borderwidth * 2 + tta[i].d_inf.Height), 0.5f, 1.0f, (float)(1.0 + ((float)borderwidth)/tta[i].d_inf.Width),   (float)(1.0 + ((float)borderwidth)/tta[i].d_inf.Height)},
				{(float)((float)dst.x),									  (float)((float)dst.y + borderwidth * 2 + tta[i].d_inf.Height), 0.5f, 1.0f, (float)(-((float)borderwidth)/tta[i].d_inf.Width),	  (float)(1.0 + ((float)borderwidth)/tta[i].d_inf.Height)},
			};
		const int num_verts = sizeof(vertices)/sizeof(*vertices);
		void *p_vb=NULL;
		hr = vertbuff->Lock(0, sizeof(vertices), &p_vb, 0);
		memcpy(p_vb, vertices, sizeof(vertices));
		vertbuff->Unlock();
		hr = d3ddev->SetFVF(VertexTypeFVF);
		d3ddev->SetStreamSource(1, vertbuff, 0, 0);
		d3ddev->SetTexture(0, tta[i].normal);
		d3ddev->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_WRAP);
		d3ddev->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_WRAP);
		d3ddev->DrawPrimitive(D3DPT_TRIANGLEFAN, 0, num_verts-2);
		dst.x += tta[i].d_inf.Width + borderwidth * 2;
	}
	normal_render->EndScene(0);
	vertbuff->Release();
	LPDIRECT3DSURFACE9 tmp_surf = NULL;
	diffuse_dxt5_texture->GetSurfaceLevel(0, &tmp_surf);
	printf("Calling LoadSurfaceFromSurface on Diffuse texture\n");
	D3DXLoadSurfaceFromSurface(tmp_surf, NULL, NULL, diffuse_render_surface, NULL, NULL, D3DX_DEFAULT, 0);
	printf("Saving Diffuse Texture\n");
	D3DXFilterTexture(diffuse_dxt5_texture, NULL, D3DX_DEFAULT, D3DX_FILTER_BOX);
	hr = D3DXSaveTextureToFile("diffuse.dds", D3DXIFF_DDS, diffuse_dxt5_texture, NULL);
	tmp_surf->Release();
	diffuse_dxt5_texture->Release();
	diffuse_render->Release();
	diffuse_render_surface->Release();
	normal_x1r5g5b5_texture->GetSurfaceLevel(0, &tmp_surf);
	printf("Calling LoadSurfaceFromSurface on Normal texture\n");
	D3DXLoadSurfaceFromSurface(tmp_surf, NULL, NULL, normal_render_surface, NULL, NULL, D3DX_DEFAULT, 0);
	printf("Saving Normal Texture\n");
	D3DXFilterTexture(normal_x1r5g5b5_texture, NULL, D3DX_DEFAULT, D3DX_FILTER_BOX);
	hr = D3DXSaveTextureToFile("normal.dds", D3DXIFF_DDS, normal_x1r5g5b5_texture, NULL);
	tmp_surf->Release();
	normal_x1r5g5b5_texture->Release();
	normal_render->Release();
	normal_render_surface->Release();
	printf("Done Saving Texture\n");
	FILE *diffuse = fopen("diffuse.dds","rb");
	FILE *normal = fopen("normal.dds","rb");
	fseek(diffuse,0,SEEK_END);
	int diffusesize = ftell(diffuse);
	fseek(diffuse,0,SEEK_SET);
	fseek(normal,0,SEEK_END);
	int normalsize = ftell(normal);
	fseek(normal,0,SEEK_SET);
	StreamSize = diffusesize + normalsize + (trailer.size() * sizeof(TerrainTextureEntry)) + 0x1C;
	StreamData = new char[StreamSize];
	memset(StreamData,0,StreamSize);
	((unsigned int *)StreamData)[0] = 0;
	((unsigned int *)StreamData)[1] = trailer.size();
	((unsigned int *)StreamData)[2] = diffusesize + normalsize + 0x1C;
	((unsigned int *)StreamData)[3] = 0x1C;
	((unsigned int *)StreamData)[4] = diffusesize;
	((unsigned int *)StreamData)[5] = diffusesize + 0x1C;
	((unsigned int *)StreamData)[6] = normalsize;
	RelocationEntries.push_back(0xC);
	RelocationEntries.push_back(0x14);
	RelocationEntries.push_back(0x8);
	fread(&StreamData[28],diffusesize,1,diffuse);
	fread(&StreamData[28+diffusesize],normalsize,1,normal);
	for (unsigned int i = 0;i < tta.size();i++)
	{
		tta[i].diffuse->Release();
		tta[i].normal->Release();
	}
	tta.clear();
	TerrainTextureEntry *tte = (TerrainTextureEntry *)&StreamData[28+diffusesize+normalsize];
	for (unsigned int i = 0;i < trailer.size();i++)
	{
		memcpy(&tte[i],&trailer[i],sizeof(TerrainTextureEntry));
	}
}
