// TCTd.cpp : super multizone tool !
//


#include <Stuff\StuffHeaders.hpp>
#include <MLR\MLRTexturePool.hpp>
#include <DLLPlatform\DLLPlatform.hpp>

#include <ElementRenderer\ElementRenderer.hpp>
#include <ElementRenderer\GroupElement.hpp>
#include <ElementRenderer\ListElement.hpp>
#include <ElementRenderer\SwitchElement.hpp>
#include <ElementRenderer\ShapeElement.hpp>
#include <MLR\MLRHeaders.hpp>
#include <gosFX\gosFX.hpp>

#include "ImageLib\image.h"

#include "HFslimMZ.hpp"

HFSlimMZ  *theTerrain;

void
	MakeBSP(
		Stuff::DynamicArrayOf<Stuff::Plane> *planes,
		WORD *plane_count,
		ElementRenderer::GroupElement *tile,
		MemoryStream *stream
	);

char theName[1024];

char contentPath[1024];
char inputPath[1024];

char texturePath[1024];
char outputPath[1024];

void FileExport(HFSlimMZ*, const char *fname);
void FileExportMaster(int, int, Scalar, Scalar, const char *fname);
void ImportMegabasetexture(Page *page);

FILE *analyzeFile;

void __stdcall InitializeGameEngine()
{
	//
	//---------------------
	// Initialize libraries
	//---------------------
	//
	Stuff::InitializeClasses();
	MidLevelRenderer::InitializeClasses(32768*16);
	gosFX::InitializeClasses();
	ElementRenderer::InitializeClasses();

	theTerrain = new HFSlimMZ;
	Register_Pointer(theTerrain);

	analyzeFile = fopen("analyze.txt", "wt");
	Verify(analyzeFile);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void __stdcall DoGameLogic()
{
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void __stdcall UpdateDisplay()
{
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void __stdcall TerminateGameEngine()
{
	Unregister_Pointer(theTerrain);
	delete theTerrain;

	fclose(analyzeFile);

	//
	//-------------------
	// Turn off libraries
	//-------------------
	//
	ElementRenderer::TerminateClasses();
	gosFX::TerminateClasses();
	MidLevelRenderer::TerminateClasses();
	Stuff::TerminateClasses();
}

//
// Setup the GameOS structure
//
void __stdcall GetGameOSEnvironment( char* CommandLine )
{
	CommandLine = CommandLine;
	Environment.applicationName			= "TCT";
	Environment.directoryPath			=	"\\Tools\\TCTb";
	Environment.screenWidth				= 640;
	Environment.screenHeight			= 480;
	Environment.fullScreen				= FALSE;
	Environment.bitDepth				= 16;
	Environment.debugLog				= "TCTLog.txt";
	Environment.spew					= "";			//"GameOS*";

	Environment.UpdateRenderers			= UpdateDisplay;
	Environment.DoGameLogic				= DoGameLogic;
	Environment.InitializeGameEngine	= InitializeGameEngine;
	Environment.TerminateGameEngine		= TerminateGameEngine;

	Environment.Renderer				= 0;
	Environment.FullScreenDevice		= 0;
	
	Environment.AntiAlias				= 0;			// true/false - Enable full screen antialiasing
	
	//
	// Texture infomation
	//
	Environment.Texture_S_256			= 13;
	Environment.Texture_S_128			= 12;
	Environment.Texture_S_64			= 1;
	Environment.Texture_S_32			= 1;
	Environment.Texture_S_16			= 1;
	
	Environment.Texture_K_256			= 1;
	Environment.Texture_K_128			= 1;
	Environment.Texture_K_64			= 1;
	Environment.Texture_K_32			= 1;
	Environment.Texture_K_16			= 1;
	
	Environment.Texture_A_256			= 1;
	Environment.Texture_A_128			= 1;
	Environment.Texture_A_64			= 1;
	Environment.Texture_A_32			= 1;
	Environment.Texture_A_16			= 1;
}

int
	__cdecl main(int argc, char *argv[])
{
	InitGameOS(NULL, NULL, "blabla");
	InitializeGameEngine();

	for(int i=1;i<argc;i++)
	{
		if(NULL!=strstr(argv[i], "-bsp") || NULL!=strstr(argv[i], "/bsp"))
		{
			doBSP = true;
		}
	}



	//
	//--------------------------------------------------------------
	// Open up the .ini file, and figure out how many map to convert
	//--------------------------------------------------------------
	//
	{
		NotationFile config_file("TCTd.ini");
		NotationFile::PageIterator *maps = config_file.MakePageIterator();
		Check_Object(maps);

		//
		//----------------------------
		// Convert the maps one by one
		//----------------------------
		//
		Page *map_to_convert;
		while ((map_to_convert = maps->ReadAndNext()) != NULL)
		{
			Check_Object(map_to_convert);
			const char* map = map_to_convert->GetName();
			Check_Pointer(map);

			const char *path;
			strcpy(contentPath, "Content\\");
			strcpy(texturePath, "Textures\\");

			const char *default_path = "\\";
			if(!map_to_convert->GetEntry("InputPath", &path))
			{
				path = default_path;
			}
			strncpy(inputPath, path, 1024);

			fprintf(stdout, "Inputpath: %s\n", inputPath);

			gos_PushCurrentHeap(MidLevelRenderer::Heap);
			MidLevelRenderer::TGAFilePool *pool = new MidLevelRenderer::TGAFilePool(texturePath);
			Check_Object(pool);
			MidLevelRenderer::MLRTexturePool::Instance =
				new MidLevelRenderer::MLRTexturePool(pool);
			Check_Object(MidLevelRenderer::MLRTexturePool::Instance);
			gos_PopCurrentHeap();

			path = map;
			strncpy(theName, path, 1024);
			fprintf(stdout, "Mapname: %s\n", theName);

			float dX, dY, dZ;
			if(!map_to_convert->GetEntry("DeltaX", &dX))
			{
				dX = 5.0f;
			}
			if(!map_to_convert->GetEntry("DeltaY", &dY))
			{
				dY = 5.0f;
			}
			if(!map_to_convert->GetEntry("DeltaZ", &dZ))
			{
				dZ = 5.0f;
			}
			fprintf(stdout, "Heighfield size: %f %f %f\n", dX, dY, dZ);
			
			theTerrain->SetScale(dX, dY, dZ);

			fprintf(stdout, "Start importing the mega texture.\n");

			if(doBSP==false)
			{
				if(!map_to_convert->GetEntry("WaterLevel", &waterLevel))
				{
					doWater = false;
				}
				else
				{
					doWater = true;
					fprintf(stdout, "Waterlevel found at: %f.\n", waterLevel);
				}
			}
			else
			{
				doWater = false;
			}

			ImportMegabasetexture(map_to_convert);
		}
	}

	TerminateGameEngine();
	ExitGameOS();
	return 0;
}

bool
	ImportBitmap(Image &timg, int iRBX, int optPolys, int bucketSize, int smooth)
{
	if(timg.GetWidth()!=timg.GetHeight()) 
	{
		fprintf(stdout, "Bitmap Must Be Square - Dimension Error\n");
		return false;
	}
	
	if(timg.GetBpp()<8)
	{
		fprintf(stdout, "Bitmap Must Be At Least 8 bit - Depth Error\n");
		return false;
	}
	else
	{
		if(timg.GetBpp()>8) //Not a Bitmapped Image Resolve
		{
			RMETHOD rmd;

			switch(iRBX)
			{
				case 0:
					rmd=RMD_REDC;
				break;
				case 1:
					rmd=RMD_GREENC;
				break;
				case 2:
					rmd=RMD_BLUEC;
				break;
				case 3:
					rmd=RMD_ALPHAC;
				break;
				case 4:
					rmd=RMD_MAX;
				break;
				case 5:
					rmd=RMD_BHT;
				break;
				default:
					rmd=RMD_MAX;
			}

			timg.ReduceTo8Bit(rmd);
		}	

		Verify(timg.GetWidth() % 256 == 0);
		Verify(timg.GetHeight() % 256 == 0);

		theTerrain->FillFromImage(timg, smooth);
		fprintf(stdout, "Heightfield image loaded.\n");

		int xDim, zDim;
		theTerrain->GetZoneDimensions(&xDim, &zDim);
		Verify((optPolys > 0) && (bucketSize > 0));
		theTerrain->OptimizeHField(xDim*zDim*optPolys/2, bucketSize);


		fprintf(stdout, "Terrain optimized.\n");

	}
	return true;
}

bool
	ImportBitmap(const char *fname, int iRBX, int optPolys, int bucketSize, int smooth)
{
	Image timg;
	char tfn[MAX_PATH];
	
	strcpy(tfn, fname);
	timg.Load(tfn);
	return ImportBitmap(timg, iRBX, optPolys, bucketSize, smooth);
}

void
	ImportMegabasetexture(
		Page *map_to_convert
	) 
{
	char buf[MAX_PATH];
	
	GetCurrentDirectory(MAX_PATH, buf);
	
	Check_Object(map_to_convert);

	const char *hf_name;
	if(!map_to_convert->GetEntry("HeightField", &hf_name))
	{
		SetCurrentDirectory(buf);
		return;
	}

	int polyCount = 5000;
	map_to_convert->GetEntry("PolyCount", &polyCount);
	int bucketSize = 64;
	map_to_convert->GetEntry("BucketSize", &bucketSize);
	int smoothPass = 2;
	map_to_convert->GetEntry("SmoothingPasses", &smoothPass);

	int xDim, zDim;
	char hf_path[1024];
	strcpy(hf_path, inputPath);
	strcat(hf_path, hf_name);
	if(false==ImportBitmap(hf_path, 4, polyCount, bucketSize, smoothPass))
	{
		SetCurrentDirectory(buf);
		return;
	}

	int borderPix = 0;
	map_to_convert->GetEntry("BorderPixels", &borderPix);
	int nrOfLevels = 1;
	map_to_convert->GetEntry("NumberOfLevels", &nrOfLevels);
	fprintf(stdout, "HF: %s pc: %d bs: %d l: %d\n", hf_name, polyCount, bucketSize, nrOfLevels);

	theTerrain->SetAllDepth(nrOfLevels);

	theTerrain->GetZoneDimensions(&xDim, &zDim);

	const char *mega_detail_name;
	if(!map_to_convert->GetEntry("DetailTexture", &mega_detail_name))
	{
		SetCurrentDirectory(buf);
		return;
	}
	char detailName[1024];
	sprintf(detailName, "Maps\\%s\\%s", theName, mega_detail_name);

	char underWaterDetailName[1024];
	char waterTextureName[1024];
	char waterDetailName[1024];

	const char *under_water_detail_name;
	const char *water_texture_name;
	const char *water_detail_texture_name;

	Scalar waterTextureRatio = 4.0f;
	Scalar waterDetailTextureRatio = 4.0f;

	if(doWater==true)
	{

		if(map_to_convert->GetEntry("UnderwaterdetailTexture", &under_water_detail_name))
		{
			sprintf(underWaterDetailName, "Maps\\%s\\%s", theName, under_water_detail_name);
			under_water_detail_name = underWaterDetailName;
		}
		else
		{
			under_water_detail_name = NULL;
		}


		if(map_to_convert->GetEntry("WaterTexture", &water_texture_name))
		{
			sprintf(waterTextureName, "Maps\\%s\\%s", theName, water_texture_name);
			water_texture_name = waterTextureName;
		}
		else
		{
			water_texture_name = NULL;
		}

		if(map_to_convert->GetEntry("WaterDetailTexture", &water_detail_texture_name))
		{
			sprintf(waterDetailName, "Maps\\%s\\%s", theName, water_detail_texture_name);
			water_detail_texture_name = waterDetailName;
		}
		else
		{
			water_detail_texture_name = NULL;
		}

		map_to_convert->GetEntry("WaterTextureRatio", &waterTextureRatio);
		map_to_convert->GetEntry("WaterDetailRatio", &waterDetailTextureRatio);

		theTerrain->SetWaterInfo(water_texture_name, waterTextureRatio);
	}


	Scalar ratio = 16.0f;
	map_to_convert->GetEntry("DetailRatio", &ratio);
	Scalar dStart = 32.0f;
	map_to_convert->GetEntry("DetailFadeBegin", &dStart);
	Scalar dEnd = 64.0f;
	map_to_convert->GetEntry("DetailFadeEnd", &dEnd);

	const char *mega_alpha_name;
	MLRState::AlphaMode alpha;

	if(map_to_convert->GetEntry("DetailAlphaMode", &mega_alpha_name))
	{
		if(!strncmp(mega_alpha_name, "OneOne", sizeof("OneOne")))
		{
			alpha = MLRState::OneOneMode;
		} else
		if(!strncmp(mega_alpha_name, "AlphaInvAlpha", sizeof("AlphaInvAlpha")))
		{
			alpha = MLRState::AlphaInvAlphaMode;
		} else
		if(!strncmp(mega_alpha_name, "OneInvAlpha", sizeof("OneInvAlpha")))
		{
			alpha = MLRState::OneInvAlphaMode;
		} else {
			alpha = MLRState::AlphaOneMode;
		}
	}
	else
	{
		alpha = MLRState::AlphaOneMode;
	}

	theTerrain->SetDetailTextureInfo(
		detailName,
		under_water_detail_name,
		0.0f,
		0.0f,
		waterDetailTextureRatio,
		waterDetailTextureRatio,
		dStart,
		dEnd,
		alpha
	);


	theTerrain->SetBorderPixels(borderPix);

	theTerrain->SetWaterDetailTextureInfo(
		water_detail_texture_name,
		0.0f,
		0.0f,
		ratio,
		ratio,
		dStart,
		dEnd,
		alpha
	);

	if(true == theTerrain->SetMegaTexture(
					nrOfLevels,
					bucketSize,
					theName,
					outputPath)
				)
	{
		FileExport(theTerrain, outputPath);
	}

	sprintf(outputPath, "%sMaps\\%s\\%s.erf", contentPath, theName, theName);
	FileExportMaster(xDim, zDim, theTerrain->GetXScale(), theTerrain->GetZScale(), outputPath);

	SetCurrentDirectory(buf);
}

bool Debug_Tile=false;
bool Debug_Zone=false;

void
	ZoneExport(
		ElementRenderer::GridElement *terrain,
		const char* zone_filename
	)
{
	Check_Object(terrain);
	Check_Pointer(zone_filename);

	//
	//--------------------------------------------------------
	// Make a memory stream array to avoid reallocating memory
	//--------------------------------------------------------
	//
	DynamicArrayOf<DynamicMemoryStream *> tile_streams(64);
	int triangle_count = terrain->CountTriangles();
	Verify(triangle_count < 32768);
	DynamicArrayOf<Plane> planes(triangle_count*2);
	WORD plane_count = 0;

	//
	//---------------------------
	// Step through all the tiles
	//---------------------------
	//
	int t;
	for (t=0; t<64; ++t)
	{
		tile_streams[t] = new DynamicMemoryStream;
		Check_Object(tile_streams[t]);
		ElementRenderer::GroupElement *tile = 
			Cast_Object(
				ElementRenderer::GroupElement*,
				terrain->GetIndexedElement(t)
			);
		Check_Object(tile);

		//
		//-----------------------------------------------
		// For each tile, run the collision process on it
		//-----------------------------------------------
		//
		fprintf(stdout, "\r  Working on tile %d", t);
		MakeBSP(&planes, &plane_count, tile, tile_streams[t]);
	}
	fprintf(stdout, "\r  Used %d planes       \n", plane_count);

	//
	//------------------------------------------
	// Copy the stream info into the output file
	//------------------------------------------
	//
	Stuff::FileStream zone_stream(zone_filename, Stuff::FileStream::WriteOnly);
	zone_stream << plane_count;
	zone_stream.WriteBytes(planes.GetData(), plane_count * sizeof(Plane));
	for (t=0; t<64; ++t)
	{
		Check_Object(tile_streams[t]);
		tile_streams[t]->Rewind();
		zone_stream << tile_streams[t]->GetSize() << *tile_streams[t];
		Check_Object(tile_streams[t]);
		delete tile_streams[t];
	}
}

void
	FileExport(HFSlimMZ *tT, const char *output_path) 
{
	//
	//---------------------------------------------------
	// Analyze the set file and extract the useful pieces
	//---------------------------------------------------
	//

	int i, j, xDim, zDim;
	tT->GetZoneDimensions(&xDim, &zDim);

	Stuff::DynamicArrayOf<ElementRenderer::GroupElement*> *waterArray;

	waterArray = tT->GetWaterArray();

	Stuff::DynamicArrayOf<ElementRenderer::GridElement*> *terrainArray;

	terrainArray = tT->GetTerrainArray();

	ElementRenderer::GridElement *terrain;
	
	for(j=0;j<zDim;j++)
	{
		for(i=0;i<xDim;i++)
		{
			terrain = (*terrainArray)[j*zDim+i];
			Check_Object(terrain);

			if(doBSP==true)
			{
				fprintf(stdout, "Processing zone %c%c\n", 'A'+j, 'A'+i);
				sprintf(outputPath, "%sMaps\\%s\\%c%c.bsp", contentPath, theName, 'A'+j, 'A'+i);
				ZoneExport(terrain, outputPath);
			}
			else
			{
				sprintf(outputPath, "%sMaps\\%s\\%c%c.erf", contentPath, theName, 'A'+j, 'A'+i);

				Stuff::FileStream element_stream(output_path, Stuff::FileStream::WriteOnly);

				ElementRenderer::WriteERFVersion(&element_stream);

				int row, col;
				for (row=0; row<8; ++row)
				{
					for (col=0; col<8; ++col)
					{
						ElementRenderer::GroupElement *group =
							Cast_Object(
								ElementRenderer::GroupElement*,
								terrain->GetIndexedElement(row, col)
							);
						Stuff::ChainIteratorOf<Element*>* iterator = group->MakeIterator();
						Check_Object(iterator);
						Element *element = iterator->GetCurrent();
						Check_Object(element);
						Verify(element->GetLocalToWorld() == Stuff::LinearMatrix4D::Identity);
						ElementRenderer::StateChange *state = new ElementRenderer::StateChange;
						state->SetLightingMode(ElementRenderer::StateChange::TerrainMode);
						element->AdoptStateChange(state);
						element->Save(&element_stream);
						Check_Object(iterator);
						delete iterator;
					}
				}
			}
		}
	}
}

void
	FileExportMaster(int xDim, int zDim, Scalar xGrid, Scalar zGrid, const char *output_path) 
{
	gos_PushCurrentHeap(ElementRenderer::Heap);
	ElementRenderer::GridElement *terrain = new ElementRenderer::GridElement(xDim, zDim, 0.0f, 0.0f, xDim*256*xGrid, zDim*256*zGrid);
	terrain->SetSize(0);
	gos_PopCurrentHeap();

	//
	//---------------------------------------------------
	// Analyze the set file and extract the useful pieces
	//---------------------------------------------------
	//

	terrain->SetRootMode();

	Stuff::FileStream element_stream(output_path, Stuff::FileStream::WriteOnly);
	ElementRenderer::WriteERFVersion(&element_stream);
	terrain->Save(&element_stream);
} // end of export
