#include "SeperateCubemappedPlanetReader.h"

#include "OgreResourceGroupManager.h"

namespace Ogre {

	SeperateCubemappedPlanetReader::SeperateCubemappedPlanetReader()
	{
	}
	SeperateCubemappedPlanetReader::~SeperateCubemappedPlanetReader()
	{}

	void SeperateCubemappedPlanetReader::openFiles(String *filenames){
		int i;
		for (i=0;i<6;i++){
			openFile(filenames[i],heightmapsFile[i]);
		}
	}

	void SeperateCubemappedPlanetReader::openFilesForWriting(String *filenames){
		int i;
		for (i=0;i<6;i++){
			openFileForWriting(filenames[i],heightmapsFile[i]);
		}
	}


	void SeperateCubemappedPlanetReader::preprocessFiles(String *filenames){
		int i;
		for (i=0;i<6;i++)
		{
			if (!isValidMap(filenames[i], i))
				throw String("ERROR : Not a valid input heightmap!");
		}
		for (i=0;i<6;i++)
		{
			preprocessFile(i);
		}
	}
	bool SeperateCubemappedPlanetReader::isValidMap(const String &filename, int imageIdx) 
 	{
		heightmapsImage[imageIdx] = new Image();
		heightmapsImage[imageIdx]->load(filename, String("ClipMap"));

		if (heightmapsImage[imageIdx]->getFormat() != PF_L8) 
			return false;

		int osx = heightmapsImage[imageIdx]->getWidth();
		int osy = heightmapsImage[imageIdx]->getHeight();
		if (osx != osy) 
			return false;

		int sx = osx / REGIONSIZE;
		int sy = osy / REGIONSIZE;

		if ((sx * REGIONSIZE + 1 != osx) ||
			(sy * REGIONSIZE + 1 != osy)) 
			return false;

		return true;
	}
	void SeperateCubemappedPlanetReader::preprocessFile(int imageIdx)
	{		
		unsigned char* bitmap = heightmapsImage[imageIdx]->getData();
		//	We already have passed the equality of sides test, so we can assume they 
		//	are the same
		int size = heightmapsImage[imageIdx]->getWidth();
		saveHeader(heightmapsFile[imageIdx], size);
		int depth =(int)((Real)(log((Real)size))/(Real)(log((Real)2))+1);
		int i;
		for (i=0 ; i<depth; i++){
			interpolateLevel (i, bitmap, size, depth, heightmapsFile[imageIdx]);
		}
		std::cout << "Preprocessing file done" << std::endl;
	}

	//TODO We need a Vector2 here
	Vector2 SeperateCubemappedPlanetReader::getTextureCoordinates(int x, int y, int level, int map) const 
	{
		return Vector2 (x / (Real)(getLevelSize(level)-1),
					    (y / (Real)(getLevelSize(level)-1)+map)/(Real)6);
	}
	int SeperateCubemappedPlanetReader::getGeometryDepth() const 
	{
		//TODO Make this foolproof so that if it used before the vertexBuffers are 
		//initialized,  it doesn't crash
		assert (vertexBuffers[0]);
		return vertexBuffers[0]->getDepth();
	}

	int SeperateCubemappedPlanetReader::getVertexPosition(int x,int y,int level,int map) const
	{
		return PlanetReader::getVertexPosition(x,y,level);
	}

	void SeperateCubemappedPlanetReader::interpolateLevel(const int level, 
														  unsigned char* bitmap, 
														  const int size, 
														  const int depth,
														  FILE *&heightmap)
	{
		const int levelsize = getLevelSize(level);
		const int stepsize = 1<<(depth-level-1);
		const int sampleRange = (level<depth-1)?1<<(depth-level-2): 0;
		
		for (int j=0;j<levelsize;j++)
		{
			for (int i=0;i<levelsize;i++)
			{
				savePixel(i, j, level,
						  resamplePixel (i*stepsize,
										  j*stepsize,
										  sampleRange,
										  bitmap, 
										  size), 
										  heightmap);
			}
		}
	}

	Real SeperateCubemappedPlanetReader::resamplePixel(const int x, const int y, 
													const int range, 
													unsigned char* bitmap, 
													const int size)
	{
		int amount = 0;
		Real height=0;
		for (int j=y-range;j<=y+range;j++)
		{
			for (int i=x-range;i<=x+range;i++)
			{
				if ((i>=0)&&(i<size)&&(j>=0)&&(j<size))
				{
					amount++;
					height += bitmap[j*size+i];
				}
			}
		}

		if (amount == 0)
		{
			std::cout << "ERROR : no heightvalues found!" << std::endl;
			height = 0;
			amount = 1;
		}
		return height / (Real)  amount;
	}

	//TODO group for better writing performance
	void SeperateCubemappedPlanetReader::savePixel(int x, int y, int level, Real height, FILE *&mapfile)
	{
		fseek(mapfile, PlanetReader::getVertexPosition(x, y, level), SEEK_SET);
		int nwrite = fwrite(&height, sizeof(Real), 1, mapfile);
		if (nwrite == 0)
		{
			std::cout << "ERROR : cannot write " << x << "," << y << " level " << level << " height " << height << std::endl;
		}
	}

	

	bool SeperateCubemappedPlanetReader::initialise(const String& filename)
	{
		ConfigFile config;

		config.load (filename, String("ClipMap"), String("\t:="), true);
		String cfgFileName = config.getSetting("planetConfig") + ".world.cfg";
		String savemap = config.getSetting("SaveClipmapMap");
		bool saveClipmap = (savemap == "YES")||(savemap == "TRUE")||(savemap == "yes")||(savemap == "true");
		config.clear ();

		config.load (cfgFileName, String("ClipMap"), String("\t:="), true);
		mScale.x = atof(config.getSetting("Scale.x").c_str());
		mScale.y = atof(config.getSetting("Scale.y").c_str());
		mScale.z = atof(config.getSetting("Scale.z").c_str());
		mHeightScale = atof(config.getSetting("Scale.height").c_str());
		if (mScale.x == 0) 
			mScale.x = 1.0;
		if (mScale.y == 0) 
			mScale.y = 0.25;
		if (mScale.z == 0) 
			mScale.z = 1.0;
		if (mHeightScale == 0) 
			mHeightScale = 1.0;

		mOffset.x = atof(config.getSetting("World.x").c_str());
		mOffset.y = atof(config.getSetting("World.y").c_str());
		mOffset.z = atof(config.getSetting("World.z").c_str());
		mHeightOffset = atof(config.getSetting("World.height").c_str());

		clipRegion = atof(config.getSetting("ClipRegion").c_str());
		if (clipRegion == 0) 
			clipRegion = 10;


		String clipmap[6];

		clipmap[0] = config.getSetting("ClipmapMap1");
		clipmap[1] = config.getSetting("ClipmapMap2");
		clipmap[2] = config.getSetting("ClipmapMap3");
		clipmap[3] = config.getSetting("ClipmapMap4");
		clipmap[4] = config.getSetting("ClipmapMap5");
		clipmap[5] = config.getSetting("ClipmapMap6");

		if (!saveClipmap)
		{
			String savemap = config.getSetting("SaveClipmapMap");
			saveClipmap = (savemap == "YES")||(savemap == "TRUE")||(savemap == "yes")||(savemap == "true");
		}
		
		int i;
		String heightmap[6];
		if (saveClipmap)
		{	    	
			openFilesForWriting(clipmap);
			heightmap[0] = config.getSetting("HeightMap1");
			heightmap[1] = config.getSetting("HeightMap2");
			heightmap[2] = config.getSetting("HeightMap3");
			heightmap[3] = config.getSetting("HeightMap4");
			heightmap[4] = config.getSetting("HeightMap5");
			heightmap[5] = config.getSetting("HeightMap6");
			preprocessFiles(heightmap);
			std::cout << "generating done" << std::endl;
		} 
		else 
		{
			std::cout << "LOADING." << std::endl;
			openFiles(clipmap);
			std::cout << "loading done" << std::endl;
		}

#if USE_TEXTURES
		// Sorry, we can use only 1 texture anymore for the moment.
		material = config.getSetting("WorldTexture").c_str();

#endif
		int size;
		if (saveClipmap) 
		{
			size = getSizeFromImage(heightmap[0]);
		} 
		else 
		{
			size = getSizeFromFile();
		}
		int depth =(int)((Real)(log((Real)size))/(Real)(log((Real)2))+1);
		for (i=0;i<6;i++)
		{
			vertexBuffers[i] = new VertexBuffer(depth);
		}
		return true;
	}

	int SeperateCubemappedPlanetReader::getSizeFromImage(String file)
	{
		Image* image = new Image();
		image->load(file, String("ClipMap"));
		return image->getWidth();
	}

	int SeperateCubemappedPlanetReader::getSizeFromFile()
	{
		int size;
		fseek(heightmapsFile[0], 0, SEEK_SET);
		fread(&size, sizeof(int), 1, heightmapsFile[0]);
		return size;
	}

	void SeperateCubemappedPlanetReader::saveHeader(FILE* &mapfile, int size)
	{
		std::cout << "Saving cubemap part..." << std::endl;

		fseek(mapfile, 0, SEEK_SET);
		fwrite(&size, sizeof(int), 1, mapfile);
	}

	Real SeperateCubemappedPlanetReader::readHeight(int x, int y, int level, int map) const 
	{
		Real height;

		fseek(heightmapsFile[map], PlanetReader::getVertexPosition(x,y,level), SEEK_SET);
		int nread = fread(&height, sizeof(Real), 1, heightmapsFile[map]);
		if (nread == 0)
		{
			std::cout << "ERROR : Could not read " << x << "," << y << " level " << level << " map " << map << " from " << PlanetReader::getVertexPosition(x,y,level) << std::endl;	
			height = 0;
		}
		return height;
	}


} // namespace Ogre
