#include "terrain.h"
#include "constants.h"
#include <stdlib.h>
#include <iostream>
#include <windows.h>
#define GLUT_DISABLE_ATEXIT_HACK 
#include <GL/glut.h>

using namespace std;

/* This file implements the functions declared in the terrain.h 
 * file.
 */

// Default constructor
Terrain::Terrain(void):rows(TERRAIN_ROWS), cols(TERRAIN_COLS), gridSize((TERRAIN_COLS - 1) * (TERRAIN_ROWS - 1) * 2)
{
	generate();
	// Initialize triangle colors to be funky.
	for (int i = 0; i < gridSize; ++i)
	{
		grid[i]->setColor(Vector(i / gridSize, (gridSize - i) / gridSize, 1));
	}
	setUpTexture();
}

// Destructor deletes the grid
Terrain::~Terrain(void)
{
	for (int i = 0; i < rows * cols; ++i)
		delete grid[i];
}

// Draw all triangles
// Hack - include two triangles "underneath" the world to prevent
// evidence of its two-dimensionality.
void Terrain::draw(void)
{
	glEnable(GL_TEXTURE_2D);
	glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
	glBindTexture(GL_TEXTURE_2D, hTexture);
	for (int i = 0; i < gridSize; ++i)
	{
		grid[i]->textureDraw();
	}

	Triangle(Vector(0, 0, 0), Vector(0, 0, MAP_SIZE), Vector(MAP_SIZE, 0, MAP_SIZE)).textureDraw();
	Triangle(Vector(0, 0, 0), Vector(MAP_SIZE, 0, 0), Vector(MAP_SIZE, 0, MAP_SIZE)).textureDraw();

	glEnd();
	glDisable(GL_TEXTURE_2D);

}

void Terrain::setUpTexture() 
{
	BITMAPINFOHEADER	bitmapInfoHeader;
	unsigned char* bitmapData;
	bitmapData = LoadBitmapFile(TERRAIN_TEXTURE, &bitmapInfoHeader);

	// create texture

	glGenTextures(1,&hTexture);
	glBindTexture(GL_TEXTURE_2D, hTexture);

	// the following two commands tell openGL how to interpolate texture
	// values between the sampled values in your texture bitmap
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

	// the following two commands tell openGL how to extend the texture
	// when needed
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
 	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

	// the following tell openGL to use the texture in place of color
	glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);

	// now give it the texture map
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, bitmapInfoHeader.biWidth,
				 bitmapInfoHeader.biHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, bitmapData);
}

// LoadBitmapFile
// desc: Returns a pointer to the bitmap image of the bitmap specified
//       by filename. Also returns the bitmap header information.
//		 No support for 8-bit bitmaps.
unsigned char* Terrain::LoadBitmapFile(char *filename, BITMAPINFOHEADER *bitmapInfoHeader)
{
	FILE *filePtr;							// the file pointer
	BITMAPFILEHEADER	bitmapFileHeader;		// bitmap file header
	unsigned char		*bitmapImage;			// bitmap image data
	int					imageIdx = 0;		// image index counter
	unsigned char		tempRGB;				// swap variable

	// open filename in "read binary" mode
	filePtr = fopen(filename, "rb");
	if (filePtr == NULL)
		return NULL;

	// read the bitmap file header
	fread(&bitmapFileHeader, sizeof(BITMAPFILEHEADER), 1, filePtr);
	
	// verify that this is a bitmap by checking for the universal bitmap id
	if (bitmapFileHeader.bfType != BITMAP_ID)
	{
		fclose(filePtr);
		return NULL;
	}

	// read the bitmap information header
	fread(bitmapInfoHeader, sizeof(BITMAPINFOHEADER), 1, filePtr);

	// move file pointer to beginning of bitmap data
	fseek(filePtr, bitmapFileHeader.bfOffBits, SEEK_SET);

	// allocate enough memory for the bitmap image data
	bitmapImage = (unsigned char*)malloc(bitmapInfoHeader->biSizeImage);

	// verify memory allocation
	if (!bitmapImage)
	{
		free(bitmapImage);
		fclose(filePtr);
		return NULL;
	}

	// read in the bitmap image data
	fread(bitmapImage, 1, bitmapInfoHeader->biSizeImage, filePtr);

	// make sure bitmap image data was read
	if (bitmapImage == NULL)
	{
		fclose(filePtr);
		return NULL;
	}

	// swap the R and B values to get RGB since the bitmap color format is in BGR
	for (imageIdx = 0; imageIdx < bitmapInfoHeader->biSizeImage; imageIdx+=3)
	{
		tempRGB = bitmapImage[imageIdx];
		bitmapImage[imageIdx] = bitmapImage[imageIdx + 2];
		bitmapImage[imageIdx + 2] = tempRGB;
		if ((imageIdx == 261900) && (imageIdx % 100 == 0))
			printf("imageIdx == %d\n",imageIdx);
	}

	// close the file and return the bitmap image data
	fclose(filePtr);
	return bitmapImage;
}

// Set color of all triangles
void Terrain::setColor(Vector color)
{
	for (int i = 0; i < gridSize; ++i)
	{
		grid[i]->setColor(color);
	}
}

// Return the grid pointer. 911 violates encapsulation
Triangle** Terrain::getGrid(void)
{
	return grid;
}

// Return the height map array. 911 violates encapsulation
double* Terrain::getMap(void)
{
	return heightmap;
}

int Terrain::getGridSize(void)
{
	return gridSize;
}

/* Generate the grid. Create an array of random numbers
 * equal in size to rows * cols and use it to generate
 * triangles. See the main loop notes for information on 
 * how that part is done.
 */
void Terrain::generate()
{
	// Generate random numbers between 0 and 2
	for (int i = 0; i < rows * cols; ++i)
	{
		heightmap[i] = (((double)rand()) / RAND_MAX) * TERRAIN_HEIGHT;
	}

	//Smooth out terrain so it's not so grody
	for (int i = 0; i < TERRAIN_SMOOTH; ++i)
	{
		smooth();
	}
	for (int i = 0; i < rows * cols; ++i)
	{
		// Have a border of zero height.
		if (((i % cols) == 0) || ((i % cols) == cols - 1) ||
			(i < cols) || (i > rows * (cols - 1)))
		{
			heightmap[i] = 0;
		}
	}


	// Generate the triangles! There will be 2 * rows * cols
	// triangles generated.
	// This loop is a bit tricky. The index variable i indexes directly into
	// heightmap, which has rows * cols entries. However, we're creating
	// triangles to put into the grid, so we need a different index variable there.
	// That's where rownum and colnum come in; they keep track of a two-dimensional
	// area with (rows - 1) * (cols - 1) entries. They determine the x and z
	// coordinates of the triangles. In addition, they serve as indices into the grid.
	// Since the grid has to hold pairs of triangles (each pair fits into one row-column
	// coordinate pair), we can't directly index into it with something like
	// (colnum + rownum*(cols - 1)); where would the second triangle go? In fact the
	// first half of the grid is devoted to triangles of the following orientation:
	// ____
	// |  /
	// | /  grid[colnum + rownum * (cols - 1)]
	// |/
	// and the second to triangles of this orientation:
	//   /|
	//  / | grid[(cols - 1) * (rows - 1) + colnum + rownum * (cols - 1)]
	// /  |
	// ----
	//
	// This is probably suboptimal, especially for retrieving all triangles in a given
	// x-z area. 911, maybe?
	// Because we need to access elements in the heightmap that are not directly
	// adjacent (i.e. elements on different rows), the bounds of the loop are rather
	// strange, as well.
	int gridIndex = 0;
	for (int rownum = 0; rownum < rows - 1; ++rownum)
	{
		for (int colnum = 0; colnum < cols - 1; ++colnum)
		{
		// Determine x and z coordinates of the triangles. Upper left corner is
		// (x1, z1), lower right is (x2, z2). 
		double x1 = ((double) colnum / cols) * MAP_SIZE;
		double x2 = ((double) (colnum + 1) / cols) * MAP_SIZE;
		double z1 = ((double) rownum / rows) * MAP_SIZE;
		double z2 = ((double) (rownum + 1) / rows) * MAP_SIZE;

		int index = rownum * cols + colnum;

		Vector tmp1(x1, heightmap[index], z1);
		Vector tmp2(x1, heightmap[index + cols], z2);
		Vector tmp3(x2, heightmap[index + 1], z1);
		Vector tmp4(x2, heightmap[index + cols + 1], z2);
		
		grid[gridIndex] = new Triangle(tmp1, tmp2, tmp3);
		grid[gridIndex + 1] = new Triangle(tmp3, tmp2, tmp4);
		gridIndex += 2;

		}
	}
}

// Given an array of doubles, smooth it out by taking each value and averaging it with
// its neighbours. This is only tricky because of the edge conditions, where the value
// has fewer than eight neighbours. Use a special averaging function to avoid this.
// This function could probably be optimized but it's only run at the beginning of the
// program, so it's not a huge problem.
// We create a second array of doubles to avoid having previous averagings affect the
// current averaging.
// To reduce the affects of the smoothing, averages are biased towards the original
// value.
void Terrain::smooth(void)
{
	double dupe[TERRAIN_ROWS * TERRAIN_COLS];	
    int i, j;
    for (i = 0; i < rows; i++)
        for (j = 0; j < cols; j++)
            dupe[j + i * cols] = gridAvg(i, j);

	// Copy dupe over
	for (int i = 0; i < rows * cols; ++i)
	{
		heightmap[i] = dupe[i];
	}
}

// Average a value in the heightmap with its neighbours. It may have three
// (corner case) or 5 (edge case) neighbours, or 8 (inner area case).
// And of course it gets averaged with itself as well.
// if i + ii > (rows - 1) then bad stuff happens, likewise if j + jj > (cols - 1)
double Terrain::gridAvg(int i, int j)
{
	double accum = 0;

	// Ensure that we don't go out of bounds
	int mini = minimum(i + 1, rows - 1);
	int minj = minimum(j + 1, cols - 1);

	int numneighbours = 0;

	int index;

	// Make certain we don't access negative indices, but don't go too high either.
	for (int ii = maximum(i - 1, 0); ii <= mini; ++ii)
	{
		for (int jj = maximum(j - 1, 0); jj <= minj; ++jj)
		{
			index = ii * cols + jj;
			accum += heightmap[index];
			numneighbours++;
		}
	}
	// Bias the average.
	accum += heightmap[i*cols + j];
	numneighbours++;
	accum /= numneighbours;
	return accum;
}


// Min and max functions. Simple.
int Terrain::minimum(int a, int b)
{
	if (a < b) return a;
	return b;
}
int Terrain::maximum(int a, int b)
{
	if (a < b) return b;
	return a;
}
