#include "Simulation.h"

#include "CodingUtils.h"
#include "MyCustomSceneNode.h"
#include "Building.h"
#include "ScaleUpAnimator.h"
#include "CityBlocks.h"
#include "TerrainBlockData.h"
#include "TerrainBlock.h"

Simulation::Simulation(IrrlichtDevice* _device)
	: device(_device)
	, m_pTerrainBlockData(NULL)
{
	driver = device->getVideoDriver();
	smgr = device->getSceneManager();
	guienv = device->getGUIEnvironment();
}

Simulation::~Simulation()
{
	//Nothing, we use destroy() method instead
}

void Simulation::initialize()
{
	//Camera initialization
	ICameraSceneNode* pCam = smgr->addCameraSceneNodeFPS(0,40.0f,0.25f);
	pCam->setPosition(vector3df(-40,130,-40));
	pCam->setTarget(vector3df(0,100,0));
	device->getCursorControl()->setVisible(false);

	//Building placement
	const int rows = CodingUtils::BLOCKS_COUNT;
	const int cols = CodingUtils::BLOCKS_COUNT;
	const float blackZoneLen = 352.f;
	const float blackZoneTopLeft = 80.f;

	for (int i=0; i<cols; i++) {
		for (int j=0; j<rows; j++) {

			float blockLen = blackZoneLen * 64.f / 512.f;
			float block_x = 64.f * i + (blackZoneTopLeft * 64.f / 512.f);
			float block_y = 64.f * j + (blackZoneTopLeft * 64.f / 512.f);

			//To spice it up a bit, we create a random number of buildings per block
			const core::rectf blockRect(block_x, block_y, block_x+blockLen, block_y+blockLen);
			const int buildingPerBlock = ((i+3)*7+j)%4;
			const int blockCount = CodingUtils::calculateSubRects(blockRect, buildingPerBlock);
			const core::rectf* blockRects = CodingUtils::getSubRectsArray();


			//Create common animator for all buildings in block
			float duration = 1.f + (float)CodingUtils::getRandomBetween(0,3000) / 1000.f;
			float waitTime = 1.5f + (float)CodingUtils::getRandomBetween(0,3000) / 1000.f;
			scene::ISceneNodeAnimator* anim = new ScaleUpAnimator(duration, waitTime);

			//Create buildings for current block. It can be zero
			for (int k=0; k<blockCount; k++)
			{
				core::rectf currRect = CodingUtils::shrinkRect(blockRects[k], 0.85f);
				float xSize = currRect.getWidth();
				float zSize = currRect.getHeight();
				const int minHeight = 30;
				const int maxHeight = 55+(rows+cols)*3;
				float height = (float)CodingUtils::getRandomBetween(minHeight,maxHeight);
				float hue = (float)CodingUtils::getRandomBetween(0,360);
				Building* pBuilding = new Building(hue, vector3df(xSize,height,zSize), smgr->getRootSceneNode(), smgr, 1000+i);

				float xPos = currRect.getCenter().X;
				float zPos = currRect.getCenter().Y;

				pBuilding->setPosition(vector3df(xPos,0,zPos));
				pBuilding->addAnimator(anim); //Comment this line to remove the "rise-up" animation behavior
				pBuilding->drop();
			}

			anim->drop();

		}
	}

	//Create Skybox
	driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, false); //No mipmaps for skybox images. There is no point in doing that.
	scene::ISceneNode* skybox=smgr->addSkyBoxSceneNode(
		driver->getTexture("assets/skybox_up.jpg"),
		driver->getTexture("assets/skybox_down.jpg"),
		driver->getTexture("assets/skybox_left.jpg"),
		driver->getTexture("assets/skybox_right.jpg"),
		driver->getTexture("assets/skybox_front.jpg"),
		driver->getTexture("assets/skybox_back.jpg"));
	driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, true); //ReEnabling mipmaps for other images to load

	//Terrain bellow
	//addTerrainFromHeighMapTexture();
	addCubeLikeTerrain();

	//Create Streets
	CityBlocks* pStreets = new CityBlocks(smgr->getRootSceneNode(), smgr, 5000);
	pStreets->drop();


	//create light source
	//This WILL NOT influence the building visuals. It just shows how a light
	//source might be added to the scene.
	scene::ISceneNode* node = 0;
	node = smgr->addLightSceneNode(0, 
		core::vector3df(CodingUtils::BLOCKS_COUNT*64.f,0,CodingUtils::BLOCKS_COUNT*64.f),
		video::SColorf(1.0f, 1.0f, 1.0f, 1.0f), 800.0f);
	scene::ISceneNodeAnimator* anim = 0;
	anim = smgr->createFlyCircleAnimator(
		core::vector3df(CodingUtils::BLOCKS_COUNT*64.f/2,0,CodingUtils::BLOCKS_COUNT*64.f/2),
		330.0f, 0.001f, core::vector3df(1,1,0));
	node->addAnimator(anim);
	anim->drop();

	// attach billboard to light
	node = smgr->addBillboardSceneNode(node, core::dimension2d<f32>(50, 50));
	node->setMaterialFlag(video::EMF_LIGHTING, false);
	node->setMaterialType(video::EMT_TRANSPARENT_ADD_COLOR);
	node->setMaterialTexture(0, driver->getTexture("assets/particlewhite.bmp"));
}

void Simulation::destroy()
{
	device->drop();

	if (m_pTerrainBlockData) {
		delete m_pTerrainBlockData;
		m_pTerrainBlockData = NULL;
	}
}

void Simulation::update()
{
	//Nothing
}
void Simulation::render()
{
	driver->beginScene(true, true, SColor(255,100,101,140));

	smgr->drawAll();
	guienv->drawAll();

	driver->endScene();
}


void Simulation::addTerrainFromHeighMapTexture()
{
	scene::ITerrainSceneNode* terrain = smgr->addTerrainSceneNode(
		"assets/terrain-heightmap.bmp",
		0,									// parent node
		-1,									// node id
		core::vector3df(0.f, -254.f, 1.f),		// position
		core::vector3df(180.f, -90.f, 0.f),		// rotation
		core::vector3df(2.6f, 1.f, 2.6f),	// scale
		video::SColor (255,255,255,255),	// vertexColor
		5,									// maxLOD
		scene::ETPS_17,						// patchSize
		4									// smoothFactor
		);
	terrain->setMaterialFlag(video::EMF_LIGHTING, false);
	terrain->setMaterialTexture(0,driver->getTexture("assets/terrain-texture.jpg"));
	terrain->setMaterialTexture(1,driver->getTexture("assets/detailmap3.jpg"));
	terrain->setMaterialType(video::EMT_DETAIL_MAP);
	terrain->scaleTexture(1.0f, 20.0f);
}

void Simulation::addCubeLikeTerrain()
{
	//TerrainBlocks will use the same Vertex and Index data.
	//In fact, they will also make use of the same material and bounding box.

	//DISCLAIMER: This way of making the terrain is SUPER inefficient.
	//			  A better approach would be to make just one cube and
	//			  set UV values bigger than one, like in CityBlocks class.
	//			  Anyway, the point of this is to show how to reuse the 
	//			  same Vertex and index data. :)

	video::ITexture* pTexture = smgr->getVideoDriver()->getTexture("./assets/terrainTile.jpg");
	m_pTerrainBlockData = new TerrainBlockData(pTexture);
	
	const float scale = 16.0f;
	const vector3df scaleVec(scale,scale,scale);
	const float deltaY = -0.01f; //To avoid "z-fighting" with "CityBlocks". Try changing it to zero and see what happens :)
	const int terrainLevels = 16;

	for (int iLevel=0; iLevel<terrainLevels; iLevel++)
	{
		const int count = (4*CodingUtils::BLOCKS_COUNT) + 1 - iLevel;

		for (int j=iLevel;j<count; j++)
		{
			for (int i=iLevel;i<count; i++)
			{
				//Skip inner blocks if they won't be visible
				if (iLevel+1 != terrainLevels) {
					if (j == iLevel || j+1 == count || i == iLevel || i+1 == count) {
						//Add block!
					} else {
						continue;
					}
				}

				TerrainBlock* pBlock = new TerrainBlock(*m_pTerrainBlockData, smgr->getRootSceneNode(), smgr, 100000);
				pBlock->setPosition(vector3df(scale*i,-(scale*(iLevel+1))+deltaY,scale*j));
				pBlock->setScale(scaleVec);
				pBlock->drop();
			}
		}
	}
}
