// TCTc.cpp : Defines the class behaviors for the application.
//


#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\MLR.hpp>
#include <MLR\MLRHeaders.hpp>

#include "ImageLib\image.h"

#include "HFslim.hpp"
HFSlim  *theTerrain;

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

DetailLevelInfo detailLevelInfo;

void FileExport(const char *fname);
void ImportMegabasetexture(NotationFile *mega_file);

FILE *analyzeFile;

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

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

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

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

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void TerminateGameEngine()
{
	fclose(analyzeFile);

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

void
	DumpScene(const char* name)
{}

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

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

	Environment.SceneDump				= DumpScene;
	
	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
	main()
{
	InitGameOS(NULL, NULL, '\0');
	InitializeGameEngine();

	FileStream *temp_output = Stuff::FileStream::MakeFileStream();
	Register_Object(temp_output);
	temp_output->Open("TCTc.ini", FileStream::ReadOnly);

	NotationFile *config_file = new NotationFile(temp_output);
	Register_Object(config_file);

	Check_Object(config_file);

	const char *texPath;
	if(!config_file->GetEntry("Miscellaneous", "Texturepath", &texPath))
	{
		return -1;
	}
	strncpy(texturePath, texPath, 1024);
	fprintf(stdout, "Texturepath: %s\n", texturePath);

	const char *default_path = "\\";
	const char *output_path;
	if(!config_file->GetEntry("Miscellaneous", "OutputPath", &output_path))
	{
		output_path = default_path;
	}
	strncpy(outputPath, output_path, 1024);

	fprintf(stdout, "Outputpath: %s\n", outputPath);

	MidLevelRenderer::TGAFilePool *pool = new MidLevelRenderer::TGAFilePool(texturePath);
	Register_Object(pool);
	MidLevelRenderer::MLRTexturePool::Instance =
		new MidLevelRenderer::MLRTexturePool(pool);
	Register_Object(MidLevelRenderer::MLRTexturePool::Instance);

	if(!config_file->GetEntry("Miscellaneous", "OutputName", &texPath))
	{
		return -1;
	}
	strncpy(theName, texPath, 1024);
	fprintf(stdout, "Output: %s\n", theName);

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

	theTerrain->SetScale(dX, dY, dZ);
	theTerrain->SetOffset(0.0f, 0.0f);

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

	Unregister_Object(config_file);
	delete config_file;

	temp_output->Close();
	Unregister_Object(temp_output);
	delete temp_output;

	Unregister_Object(theTerrain);
	delete theTerrain;

	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);
		}	

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

		Verify((optPolys > 0) && (bucketSize > 0));
		theTerrain->OptimizeHField(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(NotationFile *mega_file) 
{
	char buf[MAX_PATH];
	
	GetCurrentDirectory(MAX_PATH, buf);
	
	Check_Object(mega_file);

	const char *hf_name;
	if(!mega_file->GetEntry("HeightField", "HeightField", &hf_name))
	{
		SetCurrentDirectory(buf);
		return;
	}
	int polyCount;
	if(!mega_file->GetEntry("HeightField", "PolyCount", &polyCount))
	{
		polyCount = 0;
	}
	int bucketSize;
	if(!mega_file->GetEntry("HeightField", "BucketSize", &bucketSize))
	{
		polyCount = 0;
	}
	int smoothPass;
	if(!mega_file->GetEntry("HeightField", "SmoothingPasses", &smoothPass))
	{
		polyCount = 0;
	}

	if(false==ImportBitmap(hf_name, 4, polyCount, bucketSize, smoothPass))
	{
		return;
	}

	int borderPix;
	if(!mega_file->GetEntry("BaseTexture", "BorderPixels", &borderPix))
	{
		borderPix = 0;
	}

	int nrOfLevels;
	if(!mega_file->GetEntry("BaseTexture", "NumberOfLevels", &nrOfLevels))
	{
		nrOfLevels = 1;
	}
	fprintf(stdout, "HF: %s pc: %d bs: %d l: %d\n", hf_name, polyCount, bucketSize, nrOfLevels);

	theTerrain->SetAllDepth(nrOfLevels);
	
	const char *mega_base_name;
	if(!mega_file->GetEntry("BaseTexture", "BaseName", &mega_base_name))
	{
		SetCurrentDirectory(buf);
		return;
	}

	const char *detailLevelFileName;
	if(mega_file->GetEntry("BaseTexture", "LevelInfo", &detailLevelFileName))
	{
		char name[1024];
		strcpy(name, texturePath);
		strcat(name, detailLevelFileName);

		FILE *hd;

		hd = fopen(name, "rb");

		fread(&detailLevelInfo.width, sizeof(unsigned short), 1, hd);
		fread(&detailLevelInfo.height, sizeof(unsigned short), 1, hd);

		detailLevelInfo.array = new unsigned char [detailLevelInfo.width*detailLevelInfo.height];

		size_t how_many = fread(detailLevelInfo.array, 1, detailLevelInfo.width*detailLevelInfo.height, hd);
		if(how_many==detailLevelInfo.width*detailLevelInfo.height)
		{
			for(int i=0;i<detailLevelInfo.width*detailLevelInfo.height;i++)
			{
				if(detailLevelInfo.array[i] < 256/3)
				{
					detailLevelInfo.array[i] = 0;
				}
				else if(detailLevelInfo.array[i] < 512/3)
				{
					detailLevelInfo.array[i] = 1;
				}
				else
				{
					detailLevelInfo.array[i] = 2;
				}
			}
		}
		else
		{
			delete [] detailLevelInfo.array;
			detailLevelInfo.width = 0;
			detailLevelInfo.height = 0;

		}
		fclose(hd);
	}
	else
	{
		detailLevelInfo.width = 0;
		detailLevelInfo.height = 0;
	}

	const char *mega_detail_name;
	if(!mega_file->GetEntry("DetailTexture", "DetailTexture", &mega_detail_name))
	{
		SetCurrentDirectory(buf);
		return;
	}

	Scalar ratio;
	if(!mega_file->GetEntry("DetailTexture", "DetailRatio", &ratio))
	{
		ratio = 16.0f;
	}

	Scalar dStart;
	if(!mega_file->GetEntry("DetailTexture", "DetailFadeBegin", &dStart))
	{
		dStart = 32.0f;
	}

	Scalar dEnd;
	if(!mega_file->GetEntry("DetailTexture", "DetailFadeEnd", &dEnd))
	{
		dEnd = 64.0f;
	}

	const char *mega_alpha_name;
	MLRState::AlphaMode alpha;

	if(mega_file->GetEntry("DetailTexture", "DetailAlphaMode", &mega_alpha_name))
	{
		if(!strncmp(mega_alpha_name, "OneOne", sizeof("OneOne")))
		{
			alpha = MLRState::OneOneMode;
		} else
		if(!strncmp(mega_alpha_name, "AlphaOne", sizeof("AlphaOne")))
		{
			alpha = MLRState::AlphaOneMode;
		} else
		if(!strncmp(mega_alpha_name, "OneAlpha", sizeof("OneAlpha")))
		{
			alpha = MLRState::OneAlphaMode;
		} 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;
	}

	fprintf(stdout, "BaseTexture: %s (with %d border pixels, Detail: %s\n",
		mega_base_name,
		borderPix,
		mega_detail_name
	);

	theTerrain->SetDetailTextureInfo(
		mega_detail_name,
		0.0f,
		0.0f,
		ratio,
		ratio,
		dStart,
		dEnd,
		alpha
	);

	theTerrain->SetBorderPixels(borderPix);

	char lowestLevelTextureName[1024];
	sprintf(lowestLevelTextureName, "%s_0_0000", mega_base_name);
	MidLevelRenderer::MLRTexturePool::Instance->Add(lowestLevelTextureName);

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

	SetCurrentDirectory(buf);
}

void
	FileExport(const char *output_path) 
{
	Check_Object(theTerrain->GetGroup());

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

	Stuff::MString erf_name(theName);
	Stuff::MString path(output_path);
	
	erf_name += "_jm.erf";

	ElementRenderer::Element *terrain;
	bool terrainVisible;
	terrain = theTerrain->GetOptTerrain();
	terrainVisible = theTerrain->IsOptDisplayed();
	theTerrain->DisplayOpt(false);

	unsigned cullMode = terrain->GetCullMode();

	terrain->SetRootMode();

	Stuff::FileStream *element_stream =
		Stuff::FileStream::MakeFileStream();
	
	Register_Object(element_stream);

	Stuff::MString contents_name = path;
	contents_name += erf_name;

	element_stream->Open(contents_name, Stuff::FileStream::WriteOnly);

	ElementRenderer::WriteERFVersion(element_stream);

	terrain->Save(element_stream);

	Unregister_Object(element_stream);
	delete element_stream;

	switch(cullMode)
	{
		case ElementRenderer::Element::RootMode:
			terrain->SetRootMode();
		break;
		case ElementRenderer::Element::VolumeCullMode:
			terrain->SetVolumeCullMode();
		break;
		case ElementRenderer::Element::NeverCullMode:
			terrain->SetNeverCullMode();
		break;
		case ElementRenderer::Element::AlwaysCullMode:
			terrain->SetAlwaysCullMode();
		break;
	}

} // end of export
