#include "EDE_GridSceneNode.h"
#include <stdio.h>


/*  
    ##############################
	# Setting Up the Cell Struct #
	##############################
*/

EDE_GridSceneNode::Cell::Cell()
{
	this->topLeft = vector3df(); 
	this->topRight = vector3df(); 
	this->bottomLeft = vector3df();
	this->bottomRight = vector3df();
	this->id = 0;
	this->visible = false;
}

EDE_GridSceneNode::Cell::Cell(vector3df topLeft, vector3df topRight, vector3df bottomLeft, vector3df bottomRight, int id, bool visible, SMaterial material)
{
	this->id = id;
	this->visible = visible;

	this->bottomLeft = bottomLeft;
	this->bottomRight = bottomRight;
	this->topLeft = topLeft;
	this->topRight = topRight;
	this->center = (bottomLeft + bottomRight + topLeft + topRight) / 4;
	this->Material = material;
}

EDE_GridSceneNode::Cell::~Cell()
{

}

void EDE_GridSceneNode::Cell::RenderCell(IVideoDriver* driver, SColor color = SColor(255, 255, 255, 255))
{
	if (!visible)
		return;

	
	driver->setMaterial(this->Material);
	driver->setTransform(ETS_WORLD, matrix4());
	// Draw the Box
	driver->draw3DLine(topLeft, topRight, color); // TopLeft -> TopRight
	driver->draw3DLine(bottomLeft, bottomRight, color); // BottomLeft -> BottomRight
	driver->draw3DLine(topLeft, bottomLeft, color); // TopLeft -> BottomLeft
	driver->draw3DLine(topRight, bottomRight, color); // TopRight -> BottomRight
	
}

/*  
	##############################
	# Setting Up the Grid Class  #
	##############################
*/
EDE_GridSceneNode::EDE_GridSceneNode(ISceneNode* parent, ISceneManager* smgr, s32 id)
	: scene::ISceneNode(parent, smgr, id)
{
	gridCells = NULL;

	Material.Wireframe = false;
	Material.Lighting = false;
	Material.Thickness = 1;
	Material.FogEnable = false;
	Material.ZWriteEnable = true;
	Material.ZBuffer = true;
	Material.BackfaceCulling = true;
	Material.AntiAliasing = true;
}

void EDE_GridSceneNode::init( int width, int height, SColor color, vector3df gridPos, float cellSize)
{
	this->cellSize = cellSize;
	this->width = floor32(width / cellSize);
	this->height = floor32(height / cellSize);
	this->gridPos = gridPos;
	this->color = color;

	// Setting Up my 2D Grid Array
	gridCells = new Cell*[this->width];
	for (int i = 0; i < this->width; i++)
	{
		gridCells[i] = new Cell[this->height];
	}

	Box.reset(gridPos);

}

void EDE_GridSceneNode::createGridFlat()
{
	for (int z = 0; z < height; z++)
	{
		for (int x = 0; x < width; x++)
		{
			vector3df v1 = vector3df((gridPos.X * (x+1)) + (x*this->cellSize), gridPos.Y, (gridPos.Z * (z+1)) + (z*this->cellSize));
			vector3df v2 = vector3df((gridPos.X * (x+1)) + (x*this->cellSize) + this->cellSize, gridPos.Y, (gridPos.Z * (z+1)) + (z*this->cellSize));
			vector3df v3 = vector3df((gridPos.X * (x+1)) + (x*this->cellSize), gridPos.Y, (gridPos.Z * (z+1)) + (z*this->cellSize) + this->cellSize);
			vector3df v4 = vector3df((gridPos.X * (x+1)) + (x*this->cellSize) + this->cellSize, gridPos.Y, (gridPos.Z * (z+1)) + (z*this->cellSize) + this->cellSize);
			
			Box.addInternalPoint(v1);
			Box.addInternalPoint(v2);
			Box.addInternalPoint(v3);
			Box.addInternalPoint(v4);

			gridCells[x][z] = Cell(v1, v2, v3, v4, z + x, true, Material);
		}
	}
}

void EDE_GridSceneNode::createGridOnTerrain(ITerrainSceneNode* terrainSceneNode)
{
	for (int z = 0; z < height; z++)
	{
		for (int x = 0; x < width; x++)
		{
			vector3df v1 = vector3df((terrainSceneNode->getPosition().X * (x+1)) + (x*this->cellSize), 
				terrainSceneNode->getHeight((terrainSceneNode->getPosition().X * (x+1)) + (x*this->cellSize), (terrainSceneNode->getPosition().Z * (z+1)) + (z*this->cellSize)), 
				(terrainSceneNode->getPosition().Z * (z+1)) + (z*this->cellSize));
			vector3df v2 = vector3df((terrainSceneNode->getPosition().X * (x+1)) + (x*this->cellSize) + this->cellSize, 
				terrainSceneNode->getHeight((terrainSceneNode->getPosition().X * (x+1)) + (x*this->cellSize) + this->cellSize, (terrainSceneNode->getPosition().Z * (z+1)) + (z*this->cellSize)), 
				(terrainSceneNode->getPosition().Z * (z+1)) + (z*this->cellSize));
			vector3df v3 = vector3df((terrainSceneNode->getPosition().X * (x+1)) + (x*this->cellSize), 
				terrainSceneNode->getHeight((terrainSceneNode->getPosition().X * (x+1)) + (x*this->cellSize), (terrainSceneNode->getPosition().Z * (z+1)) + (z*this->cellSize) + this->cellSize), 
				(terrainSceneNode->getPosition().Z * (z+1)) + (z*this->cellSize) + this->cellSize);
			vector3df v4 = vector3df((terrainSceneNode->getPosition().X * (x+1)) + (x*this->cellSize) + this->cellSize, 
				terrainSceneNode->getHeight((terrainSceneNode->getPosition().X * (x+1)) + (x*this->cellSize) + this->cellSize, (terrainSceneNode->getPosition().Z * (z+1)) + (z*this->cellSize) + this->cellSize), 
				(terrainSceneNode->getPosition().Z * (z+1)) + (z*this->cellSize) + this->cellSize);

			Box.addInternalPoint(v1);
			Box.addInternalPoint(v2);
			Box.addInternalPoint(v3);
			Box.addInternalPoint(v4);

			gridCells[x][z] = Cell(v1, v2, v3, v4, z + x, true, Material);
		}
	}
}

void EDE_GridSceneNode::renderGrid(IVideoDriver* driver, SColor color)
{
	for (int z = 0; z < height; z++)
	{
		for (int x = 0; x < width; x++)
		{
			gridCells[x][z].RenderCell(driver, color);
		}
	}
}

void EDE_GridSceneNode::OnRegisterSceneNode()
{
	if (IsVisible)
		SceneManager->registerNodeForRendering(this);

	ISceneNode::OnRegisterSceneNode();
}

void EDE_GridSceneNode::render()
{
	IVideoDriver* driver = SceneManager->getVideoDriver();

	if ( !driver )
		return;

	renderGrid(driver, this->color);	
}

const aabbox3d<f32>& EDE_GridSceneNode::getBoundingBox() const
{
	return Box;
}

u32 EDE_GridSceneNode::getMaterialCount() const
{
	return 1;
}

SMaterial& EDE_GridSceneNode::getMaterial(u32 i)
{
	return Material;
}
