// 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 <ElementRenderer\GridElement.hpp>
#include <ElementRenderer\StateChange.hpp>
#include <MLR\MLRHeaders.hpp>
#include <gosFX\gosFX.hpp>

#include "ImageLib\image.h"

#include "HFslimMZ.hpp"
#include <GameOS\ToolOS.hpp>

//#define NON_DEBUG_BUG 1

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, Scalar, Scalar, const char 
*fname);
void ImportMegabasetexture(Page *page);
void ImportExportCulturalMeshs(Page *map_to_convert, const char*, const 
char*, Scalar, Scalar, Scalar, Scalar);

FILE *analyzeFile;

bool doCulturalsOnly = false;
const int culturalLocationMax = 64*1024;	//	this is INSANE
CulturalLoc culturalLoc[culturalLocationMax];
int culturalLocCount = 0;
bool culturalBillboarding[9];


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

	Stuff::ArmorLevel=1;

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

	const char *notation_file_name;
	char default_notation_file_name[] = "TCTd.ini";

	notation_file_name = default_notation_file_name;

	for(int i=1;i<argc;i++)
	{
		if(NULL!=strstr(argv[i], "-h") || NULL!=strstr(argv[i], "/h"))
		{
			fprintf(stdout, "%s - Terrain Creation Tool\n", argv[0]);
			fprintf(stdout, "If no commandline options are specified TCT is looking for a TCTd.ini in\n");
			fprintf(stdout, "the working directory and none of the options below are true.\n\n");
			fprintf(stdout, "Commandline options:\n");
			fprintf(stdout, "/h - show this help\n");
			fprintf(stdout, "/c - stops after creating Cultural.mlr\n");
			fprintf(stdout, "/p - create a precise, seamless terrain (takes much longer!)\n");
			fprintf(stdout, "/uid - use interest data, height field must be 24-bit\n\tred channel - height info\n\tgreen channel - interest data\n");
			fprintf(stdout, "/bsp - create bsp-files\n");
			fprintf(stdout, "/f filename - use specified file as TCTd.ini file\n");
			fprintf(stdout, "/s - cap the number of points on a per tile base \n");
			fprintf(stdout, "/y - cap the number of points on a per tile base including the surrounding tiles \n");
			fprintf(stdout, "/m - saves density map\n\tred channel - tile density\n\t green channel - order of insertion\n\tblue chanel - number of neighbors \n");
			fprintf(stdout, "/i - sIlent mode - no more PAUSEs \n");
			exit(0);
		}

		if(NULL!=strstr(argv[i], "-p") || NULL!=strstr(argv[i], "/p"))
		{
			fprintf(stdout, "Precise Mode requested.\n");
			doPrecise = true;
		}

		if(NULL!=strstr(argv[i], "-uid") || NULL!=strstr(argv[i], "/uid"))
		{
			fprintf(stdout, "Use interest data Mode requested.\n");
			doInterest = true;
		}

		if(NULL!=strstr(argv[i], "-s") || NULL!=strstr(argv[i], "/s"))
		{
			fprintf(stdout, "Cap triangle count Mode requested.\n");
			doTileCap = true;
		}

		if(NULL!=strstr(argv[i], "-c") || NULL!=strstr(argv[i], "/c"))
		{
			fprintf(stdout, "Stops executing after creation of Cultural.mlr.\n");
			doCulturalsOnly = true;
		}

		if(NULL!=strstr(argv[i], "-y") || NULL!=strstr(argv[i], "/y"))
		{
			fprintf(stdout, "Cap triangle count Mode 2 requested.\n");
			doTileCapYovin = true;
		}

		if(NULL!=strstr(argv[i], "-m") || NULL!=strstr(argv[i], "/m"))
		{
			fprintf(stdout, "Create density map requested. Programm will exit after bitmap creation.\n");
			doDensityMap = true;
		}

		if(NULL!=strstr(argv[i], "-i") || NULL!=strstr(argv[i], "/i"))
		{
			fprintf(stdout, "Silent mode requested. Program won't be pausing anymore.\n");
			silentMode = true;
		}

		if(NULL!=strstr(argv[i], "-bsp") || NULL!=strstr(argv[i], "/bsp"))
		{
			fprintf(stdout, "Bsp-file creation requested.\n");
			doBSP = true;
		}

		if(NULL!=strstr(argv[i], "-f") || NULL!=strstr(argv[i], "/f"))
		{
			notation_file_name = argv[++i];
			fprintf(stdout, "TCTd will use following file as ini-file: %s.\n", notation_file_name);
		}
	}



	//
	//--------------------------------------------------------------
	// Open up the .ini file, and figure out how many map to convert
	//--------------------------------------------------------------
	//
	{
		NotationFile config_file(notation_file_name);
		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::TexturePoolHeap);
			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);

			const char *map_center;
			Stuff::Scalar map_center_x, map_center_z;
			map_center_x = 0.0f;
			map_center_z = 0.0f;
			if(map_to_convert->GetEntry("MapCenter", &map_center))
			{
				sscanf(map_center, "%f %f", &map_center_x, &map_center_z);
				theTerrain->SetCenter(map_center_x, map_center_z);
			}
			else
			{
				theTerrain->SetCenter(0.0f, 0.0f);
			}

			ImportExportCulturalMeshs(map_to_convert, inputPath, theName, dX, dZ, map_center_x, map_center_z);

			if(doCulturalsOnly==false)
			{
				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);
			}
			else
			{
				fprintf(stdout,"Culturals done I am going home.");
			}
		}
	}

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

bool
	ImportBitmap(Image &timg, int iRBX, int optPolys, int bucketSize, int 
smooth, const char *density_map_name)
{
	if(timg.GetBpp()<8)
	{
		STOP(("Bitmap Must Be At Least 8 bit - Depth Error\n"));
		return false;
	}
	else
	{
		if(timg.GetBpp()>8) //Not a Bitmapped Image Resolve
		{
			if(doInterest==true)
			{
				theTerrain->FillInterestDataFromImage(timg);
				iRBX = 0;
			}

			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);
		}
		else
		{
			doInterest = false;
		}

		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, density_map_name);


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

	}
	return true;
}

bool
	ImportCulturalLocationBitmap(Image &timg, int iRBX, Scalar dX, Scalar dZ, 
Scalar xOffset, Scalar zOffset, int max, int min)
{
	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);
	}

	BYTE *img = timg.Lock();
	int w = timg.GetHeight();
	int h = timg.GetWidth();

	for(int j=0;j<w;j++)
	{
		for(int i=0;i<h;i++)
		{
			if(img[j*timg.GetPitch() + i] > 0x80)
			{
				if(culturalLocCount>=culturalLocationMax)
				{
					return true;
				}

				culturalLoc[culturalLocCount].loc.x = (w-i-1)*dX + 
4.0f*(Random::GetFraction() - 0.5f) - xOffset;
				culturalLoc[culturalLocCount].loc.y = 0.0f;
				culturalLoc[culturalLocCount].loc.z = (h-j-1)*dZ + 
4.0f*(Random::GetFraction() - 0.5f) - zOffset;

				culturalLoc[culturalLocCount].nr = min + Random::GetLessThan(max);

				culturalLoc[culturalLocCount].yaw = 
culturalBillboarding[culturalLoc[culturalLocCount].nr] ? 0xff : 
Random::GetLessThan(180);

				culturalLocCount++;
			}
		}
	}

	return true;
}

MLRShape*
	TraversErfBranch(ElementRenderer::Element *elmt)
{
	if(elmt->GetClassID() == ElementRenderer::CameraElementClassID)
	{
		return NULL;
	}

	switch(elmt->GetClassID())
	{
		case ElementRenderer::GroupElementClassID:
		{
			ElementRenderer::GroupElement *group = 
Cast_Pointer(ElementRenderer::GroupElement *, elmt);

			Stuff::ChainIteratorOf<ElementRenderer::Element*> *children = 
group->MakeIterator();
			Register_Object(children);

			ElementRenderer::Element *child = NULL;
			while ((child = children->ReadAndNext()) != NULL)
			{
				TraversErfBranch(child);
			}
			Unregister_Object(children);
			delete children;
		}
		break;

		case ElementRenderer::ListElementClassID:
		{
			ElementRenderer::ListElement *group = 
Cast_Pointer(ElementRenderer::ListElement *, elmt);

			for(int i=0;i<group->GetActiveCount();i++)
			{
				TraversErfBranch(group->GetIndexedElement(i));
			}
		}
		break;
		case ElementRenderer::GridElementClassID:
		{
			ElementRenderer::GridElement *group = 
Cast_Pointer(ElementRenderer::GridElement *, elmt);

			BYTE xSize, zSize;
			group->GetSize(&xSize, &zSize);
			for(BYTE i=0;i<zSize;i++)
				for(BYTE j=0;j<xSize;j++)
				{
					TraversErfBranch( group->GetIndexedElement(i, j));
				}
		}
		break;
		case ElementRenderer::LODElementClassID:
		{
			ElementRenderer::ListElement *group = 
Cast_Pointer(ElementRenderer::ListElement *, elmt);

			for(int i=0;i<group->GetActiveCount();i++)
			{
				TraversErfBranch(group->GetIndexedElement(i));
			}
		}
		break;
		case ElementRenderer::PointCloudElementClassID:
		{
		}
		break;
		case ElementRenderer::ScalableShapeElementClassID:
		{
			ElementRenderer::ShapeElement *group = 
Cast_Pointer(ElementRenderer::ShapeElement *, elmt);
			return group->GetMLRShape();
		}
		break;
		case ElementRenderer::ScreenQuadsElementClassID:
			{
			}
			break;

		case ElementRenderer::ShapeElementClassID:
		{
			ElementRenderer::ShapeElement *group = 
Cast_Pointer(ElementRenderer::ShapeElement *, elmt);
			return group->GetMLRShape();
		}
		break;
		case ElementRenderer::SwitchElementClassID:
		{
			ElementRenderer::ListElement *group = 
Cast_Pointer(ElementRenderer::ListElement *, elmt);

			for(int i=0;i<group->GetActiveCount();i++)
			{
				TraversErfBranch(group->GetIndexedElement(i));
			}
		}
		break;
		case ElementRenderer::TriangleCloudElementClassID:
		{
		}
		break;
	}

	return NULL;
}

void
	ImportExportCulturalMeshs(
		Page *map_to_convert,
		const char *dir,
		const char *missionName,
		Scalar dX, Scalar dZ,
		Scalar xOffset, Scalar zOffset
	)
{
	gos_PushCurrentHeap(MidLevelRenderer::ShapeHeap);
	MLRShape *outShape = new MLRShape(9);
	gos_PopCurrentHeap();
	ElementRenderer::Element *elm[3] = {NULL, NULL, NULL};
	FileStream fs[3];

	int count=0, primCount=0, primCounts[3];
	char name[1024];

	const char *fadeValues;
	if(map_to_convert->GetEntry("CulturalFadeValues", &fadeValues))
	{
		Scalar fadeIn = 50.0f, fadeOut = 80.0f;
		sscanf(fadeValues, "%f %f", &fadeIn, &fadeOut);
		theTerrain->SetCulturalFadeValues(fadeIn, fadeOut);
	}

	const char *culAname;
	if(map_to_convert->GetEntry("CulturalALocations", &culAname))
	{
		ChainOf<Note*> *nc = map_to_convert->MakeNoteChain("CulturalAObject");

		ChainIteratorOf<Note*> nci(nc);
		Note *innote;
		while (innote = nci.ReadAndNext())
		{
			const char *value;
			innote->GetEntry(&value);

			int billboarded;

			sscanf(value, "%s %d", name, &billboarded);
			MString fname(dir);

			fname += "Culturals\\";
			fname += name;

			fs[0].Open(fname);

			if(fs[0].IsFileOpened()==true)
			{
				gos_PushCurrentHeap(ElementRenderer::g_Heap);
				elm[0] = ElementRenderer::Element::Create(&fs[0], ElementRenderer::ReadERFVersion(&fs[0]));
				gos_PopCurrentHeap();

				MLRShape *shape = TraversErfBranch(elm[0]);

				if(shape!=NULL)
				{
					MLRPrimitiveBase *prim = shape->Find(0);

					MLRState state = prim->GetReferenceState();

					state.SetFogMode(0);

					prim->SetReferenceState(state);

					outShape->Add(prim);
					culturalBillboarding[count++] = billboarded>0 ? true : false;
				}
			}
		}
		delete nc;

		primCounts[0] = outShape->GetNum() - primCount;

		if(primCounts[0]>0)
		{
			MString fname(dir);
			fname += "Culturals\\";
			fname += culAname;

			Image timg;
			timg.Load(fname);

			if(false==ImportCulturalLocationBitmap(timg, 4, dX, dZ, xOffset, zOffset, primCounts[0], primCount))
			{
				STOP(("Cannot find %s!", fname));
			}
		}

		primCount = primCounts[0];
	}

	const char *culBname;
	if(map_to_convert->GetEntry("CulturalBLocations", &culBname))
	{
		ChainOf<Note*> *nc = map_to_convert->MakeNoteChain("CulturalBObject");

		ChainIteratorOf<Note*> nci(nc);
		Note *innote;
		while (innote = nci.ReadAndNext())
		{
			MString fname(dir);
			fname += "Culturals\\";

			const char *value;
			innote->GetEntry(&value);

			int billboarded;

			sscanf(value, "%s %d", name, &billboarded);

			fname += name;

			fs[1].Open(fname);

			if(fs[1].IsFileOpened()==true)
			{
				gos_PushCurrentHeap(ElementRenderer::g_Heap);
				elm[1] = ElementRenderer::Element::Create(&fs[1], ElementRenderer::ReadERFVersion(&fs[1]));
				gos_PopCurrentHeap();

				MLRShape *shape = TraversErfBranch(elm[1]);

				if(shape!=NULL)
				{
					MLRPrimitiveBase *prim = shape->Find(0);

					MLRState state = prim->GetReferenceState();

					state.SetFogMode(0);

					prim->SetReferenceState(state);

					outShape->Add(prim);
					culturalBillboarding[count++] = billboarded>0 ? true : false;
				}
			}
		}
		delete nc;

		primCounts[1] = outShape->GetNum() - primCount;

		if(primCounts[1]>0)
		{
			MString fname(dir);
			fname += "Culturals\\";
			fname += culBname;

			Image timg;
			timg.Load(fname);

			if(false==ImportCulturalLocationBitmap(timg, 4, dX, dZ, xOffset, zOffset, primCounts[1], primCount))
			{
				STOP(("Cannot find %s!", fname));
			}
		}
		primCount += primCounts[1];
	}

	const char *culCname;
	if(map_to_convert->GetEntry("CulturalCLocations", &culCname))
	{
		ChainOf<Note*> *nc = map_to_convert->MakeNoteChain("CulturalCObject");

		ChainIteratorOf<Note*> nci(nc);
		Note *innote;
		while (innote = nci.ReadAndNext())
		{
			const char *value;
			innote->GetEntry(&value);

			int billboarded;

			sscanf(value, "%s %d", name, &billboarded);
			MString fname(dir);

			fname += "Culturals\\";
			fname += name;

			fs[2].Open(fname);

			if(fs[2].IsFileOpened()==true)
			{
				gos_PushCurrentHeap(ElementRenderer::g_Heap);
				elm[2] = ElementRenderer::Element::Create(&fs[2], ElementRenderer::ReadERFVersion(&fs[2]));
				gos_PopCurrentHeap();

				MLRShape *shape = TraversErfBranch(elm[2]);

				if(shape!=NULL)
				{
					MLRPrimitiveBase *prim = shape->Find(0);

					MLRState state = prim->GetReferenceState();

					state.SetFogMode(0);

					prim->SetReferenceState(state);

					outShape->Add(prim);
					culturalBillboarding[count++] = billboarded>0 ? true : false;
				}
			}
		}
		delete nc;

		primCounts[2] = outShape->GetNum() - primCount;

		if(primCounts[2]>0)
		{
			MString fname(dir);
			fname += "Culturals\\";
			fname += culCname;

			Image timg;
			timg.Load(fname);

			if(false==ImportCulturalLocationBitmap(timg, 4, dX, dZ, xOffset, zOffset, primCounts[2], primCount))
			{
				STOP(("Cannot find %s!", fname));
			}
		}

		primCount += primCounts[2];
	}

	if(primCount>0)
	{
		MString fname(dir);

		fname += missionName;
		fname += "Culturals.mlr";

		if(gos_FileReadOnly(fname)==false)
		{
			FileStream fs(fname, FileStream::WriteOnly);

			if(fs.IsFileOpened()==true)
			{
				WriteMLRVersion(&fs);

				OBB obb;
				obb = OBB::Identity;
				obb.sphereRadius = 5.0f;

				fs << obb;

				fs << outShape->GetClassID();
				fs << (BYTE)outShape->GetNum();

				int i;
				MLRPrimitiveBase *prim;
//				MLR_I_TMesh *tmesh;

				for(i=0;i<outShape->GetNum();i++)
				{
					prim = outShape->Find(i);
					prim->Save(&fs);
//					tmesh = Cast_Object(MLR_I_TMesh*, prim);
//					tmesh->MLR_I_TMesh::Save(&fs);
				}

				char mapDataFileName[1024];
				sprintf(mapDataFileName, "Content\\Maps\\%s\\%s.data", theName, theName);
				NotationFile mapData(mapDataFileName);

				Page *page = mapData.FindPage("GameData");

				if(page!=NULL)
				{
					sprintf(mapDataFileName, "Content\\Maps\\%s\\%sCulturals.mlr", missionName, missionName);
					page->SetEntry("CulturalData", mapDataFileName);

					mapData.Save();
				}
			}
		}
		else
		{
			STOP(("File %s is read-only!", fname));
		}
	}

	if(elm[0]!=NULL)
	{
		delete elm[0];
	}

	if(elm[1]!=NULL)
	{
		delete elm[1];
	}

	if(elm[2]!=NULL)
	{
		delete elm[2];
	}
}

bool
	ImportBitmap(const char *fname, int iRBX, int optPolys, int bucketSize, int smooth, const char *density_map_name)
{
	Image timg;
	char tfn[MAX_PATH];

	strcpy(tfn, fname);
	timg.Load(tfn);
	return ImportBitmap(timg, iRBX, optPolys, bucketSize, smooth, density_map_name);
}

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

	if(bucketSize>Limits::Max_Number_Vertices_Per_Mesh/2)
	{
		bucketSize = Limits::Max_Number_Vertices_Per_Mesh/2;
	}

	int smoothPass = 2;
	map_to_convert->GetEntry("SmoothingPasses", &smoothPass);

	int xDim, zDim;
	char hf_path[1024], density_map_name[1024];
	strcpy(hf_path, inputPath);
	strcat(hf_path, hf_name);

	sprintf(density_map_name, "%sdensity_%s", inputPath, hf_name);

	if(false==ImportBitmap(hf_path, 0, polyCount, bucketSize, smoothPass, density_map_name))
	{
		SetCurrentDirectory(buf);
		return;
	}

	int borderPix = 0;
	map_to_convert->GetEntry("BorderPixels", &borderPix);
	int nrOfLevels = 3;
	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);
	strncpy(detailName, mega_detail_name, 1023);

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

	const char *under_water_detail_name = NULL;
	const char *water_texture_name = NULL;
	const char *water_detail_texture_name = NULL;

	Scalar waterTextureRatio = 4.0f;
	Scalar waterDetailTextureRatio = 4.0f;
	int waterMovieFrames = 1;
	int waterDetailMovieFrames = 1;

	if(doWater==true)
	{

		if(map_to_convert->GetEntry("UnderwaterdetailTexture", &under_water_detail_name))
		{
//			sprintf(underWaterDetailName, "Maps\\%s\\%s", theName, under_water_detail_name);
			strncpy(underWaterDetailName,  under_water_detail_name, 1023);
			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);
			strncpy(waterTextureName, water_texture_name, 1023);
			water_texture_name = waterTextureName;
		}
		else
		{
			water_texture_name = NULL;
		}

		map_to_convert->GetEntry("WaterTextureMovieFrames", &waterMovieFrames);

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

		map_to_convert->GetEntry("WaterDetailTextureMovieFrames", &waterDetailMovieFrames);

		char mapDataFileName[1024];
		sprintf(mapDataFileName, "Content\\Maps\\%s\\%s.data", theName, theName);
		NotationFile mapData(mapDataFileName);

		Page *page = mapData.FindPage("GameData");

		if(page!=NULL)
		{
			page->SetEntry("WaterLevel", waterLevel);
			if(under_water_detail_name!=NULL)
			{
				page->SetEntry("UnderWaterTexture", under_water_detail_name);
			}

			if(water_texture_name!=NULL)
			{
				page->SetEntry("WaterTexture", water_texture_name);
			}

			if(water_detail_texture_name!=NULL)
			{
				page->SetEntry("WaterDetailTexture", water_detail_texture_name);
			}

			mapData.Save();
		}

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

		theTerrain->SetWaterInfo(water_texture_name, waterTextureRatio, waterMovieFrames);
	}


	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::AlphaInvAlphaMode;
	}

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


	theTerrain->SetBorderPixels(borderPix);

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

#ifdef NON_DEBUG_BUG
	fprintf(stdout, "\nCalling SetMegaTexture");
#endif

	if(true == theTerrain->SetMegaTexture(
					nrOfLevels,
					bucketSize,
					theName,
					outputPath)
				)
	{
#ifdef NON_DEBUG_BUG
	fprintf(stdout, "\nCalling SetMegaTexture (tctd) (returned TRUE)");
#endif
		theTerrain->SetCulturals(culturalLoc, culturalLocCount);
#ifdef NON_DEBUG_BUG
	fprintf(stdout, "\nCalling SetMegaTexture (SetCulturals)");
#endif
		FileExport(theTerrain, outputPath);
#ifdef NON_DEBUG_BUG
	fprintf(stdout, "\nCalling SetMegaTexture (FileExport)");
#endif
	}
#ifdef NON_DEBUG_BUG
	fprintf(stdout, "\nCalling SetMegaTexture (done)");
#endif

	sprintf(outputPath, "%sMaps\\%s\\%s.erf", contentPath, theName, theName);
#ifdef NON_DEBUG_BUG
	fprintf(stdout, "\n%s", outputPath);
#endif
	FileExportMaster(
		xDim, zDim,
		theTerrain->GetCenterX(), theTerrain->GetCenterZ(),
		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::GridElement*> *terrainArray;

	terrainArray = tT->GetTerrainArray();

	ElementRenderer::GridElement *terrain;

	for(j=0;j<zDim;j++)
	{
		for(i=0;i<xDim;i++)
		{
			terrain = (*terrainArray)[j*xDim+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);

				if(element_stream.IsFileOpened()==true)
				{
					fprintf(stdout, "Saving file %s.\n", outputPath);
				}
				else
				{
					fprintf(stdout, "Couldn't open file %s.\n", outputPath);
				}

				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<ElementRenderer::Element*>* iterator = group->MakeIterator();
						Check_Object(iterator);
						ElementRenderer::Element *element = iterator->GetCurrent();
						Check_Object(element);
						Verify(element->GetLocalToWorld() == Stuff::LinearMatrix4D::Identity);
						ElementRenderer::StateChange *state = new ElementRenderer::StateChange;
						state->SetLightingMode(MidLevelRenderer::MLRState::TerrainLightingMode);
						element->AdoptStateChange(state);
						element->Save(&element_stream);
						Check_Object(iterator);
						delete iterator;
					}
				}
			}
		}
	}
}

void
	FileExportMaster(int xDim, int zDim, Scalar mapCenterX, Scalar mapCenterZ, Scalar xGrid, Scalar zGrid, const char *output_path)
{
	gos_PushCurrentHeap(ElementRenderer::GridElement::s_Heap);
	ElementRenderer::GridElement *terrain =
		new ElementRenderer::GridElement(
			zDim, xDim,
			-mapCenterZ, -mapCenterX,
			zDim*256*zGrid, xDim*256*xGrid
		);

	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

