#include "sampleshapesgame.h"

#include <gxframework/vertextype.h>

SampleShapesGame::SampleShapesGame()
{
	preferAntialising(4);
}

GxF32 rand(GxF32 a, GxF32 b)
{
	const GxF32 tmp = b - a;
	return (rand() % 100) / 100.0f * tmp + a;
}

void SampleShapesGame::initialize()
{
	SampleGame::initialize();

	//
	camera->lookAt(PxVec3(50, 110, 330), PxVec3(50, 20, 140));

	//mapa wysokosci
	this->prepareMaterials();
	this->createHeightField(0.5f,2,2);

	//teddy bear
	teddyMesh=content->loadRawMesh("teddy.raw");
	PxTriangleMesh* triangleMesh = createTriangleMeshFromGraphicsMesh(teddyMesh);
	teddyActor = createStaticActor(PxTransform(PxVec3(50,20,140)),PxTriangleMeshGeometry(triangleMesh),defaultPhysicsMaterial, &Material::Ruby);
	scene->addActor(*teddyActor);
}

void SampleShapesGame::release()
{
	SampleGame::release();
}

void SampleShapesGame::input(GxF32 elapsedTime)
{
	SampleGame::input(elapsedTime);

	if ( getKeyboard().isKeyPressed(Keys::Space) )
	{
		PxVec3 velocity = camera->getViewDir();
		velocity *= 150.0f;
		this->throwBox(camera->getPosition(), 2.0f, velocity, defaultPhysicsMaterial);
	}

	PxVec3 position;
	if(raycastFromCamera(position,heightFieldActor))
	{
		if(getKeyboard().isKeyPressed(Keys::Z)) this->addStackedBoxesAt(position,8,defaultPhysicsMaterial,&Material::Obsidian);
		if(getKeyboard().isKeyPressed(Keys::X)) this->addWallBoxesAt(position,5,8,defaultPhysicsMaterial,&Material::Turquoise);
		if(getKeyboard().isKeyPressed(Keys::C)) this->addCapsuleAt(position,2,1,defaultPhysicsMaterial,&Material::Turquoise);
		if(getKeyboard().isKeyPressed(Keys::V)) this->addSphereAt(position,2,defaultPhysicsMaterial,&Material::PolishedGold);
	}

	const GxF32 fps = getStepper().getFps();
	std::stringstream stream;
	stream << "Sample Shape | Fps: " << std::fixed << std::setprecision(1) << fps 
		<< " | Dynamic actors: " << scene->getNbActors(PxActorTypeSelectionFlag::eRIGID_DYNAMIC)
		<< " | Static actors: " << scene->getNbActors(PxActorTypeSelectionFlag::eRIGID_STATIC)
		<< " | Camera position: " << camera->getPosition().x << " ; " << camera->getPosition().y << " ; " << camera->getPosition().z;

	this->setTitle(stream.str());
}

void SampleShapesGame::update(GxF32 elapsedTime)
{
	SampleGame::update(elapsedTime);
}

void SampleShapesGame::render(GxF32 elapsedTime)
{
	SampleGame::render(elapsedTime);

	graphics->clear(Color::CornflowerBlue);

	effect->bind();
	effect->setProjMatrix(projectionMtx);
	effect->setViewMatrix(camera->getViewMatrix());

	//mapa wysokosci
	this->renderHeightFieldActor(heightFieldActor,heightFieldGraphicsMesh,&groundMaterial);

	//teddy bear
	this->renderTriangleMeshActor(teddyActor,teddyMesh,&teddyMaterial);

	this->renderScene(scene);

	effect->unbind();
}

void SampleShapesGame::prepareMaterials()
{	
	groundMaterial=Material::Default;
	groundMaterial.isLit=true;

	teddyMaterial=Material::Default;
	teddyMaterial.isLit=true;
	teddyMaterial.ambient=PxVec4(0.41f,0.39f,0.34f,1.0f);
	teddyMaterial.diffuse=PxVec4(0.81f,0.69f,0.54f,1.0f);
}

void SampleShapesGame::createHeightField(GxF32 heightScale, GxF32 rowScale, GxF32 columnScale)
{
	std::string path = rootDirectory;
	path.append("heightmap_x.raw");

	//ladowanie pliku z mapa
	int nbColumns = 0, nbRows = 0;
	loadRawHeightField(path.c_str(),NULL,nbColumns,nbRows); //pobieramy wymiary, nie wczytujemy

	if(nbColumns>0 && nbRows>0)
	{
		const int nbSamples=nbColumns*nbRows;
		unsigned char* data = new unsigned char[nbSamples];
		loadRawHeightField(path.c_str(),data,nbColumns,nbRows);

		PxHeightFieldSample* samples=new PxHeightFieldSample[nbSamples];
		for(int i=0;i<nbSamples;++i)
		{
			samples[i].height=PxI16(data[i]*heightScale);
			samples[i].clearTessFlag();
			samples[i].materialIndex0=0;
			samples[i].materialIndex1=0;
		}

		delete [] data;
		data=NULL;

		PxHeightFieldDesc desc;
		desc.format=PxHeightFieldFormat::eS16_TM;
		desc.nbColumns=nbColumns;
		desc.nbRows=nbRows;
		desc.samples.data=samples;
		desc.samples.stride=sizeof(PxHeightFieldSample);

		PxHeightField* heightField=physics->createHeightField(desc);
		delete [] samples;
		samples=NULL;

		PxTransform pose=PxTransform::createIdentity();
		pose.p=PxVec3(-(nbRows/2*rowScale), 0, -(nbColumns/2*columnScale));

		PxHeightFieldGeometry geometry(heightField, PxMeshGeometryFlags(), heightScale, rowScale, columnScale);
		/*PxRigidStatic* rigidStatic =*/ heightFieldActor = createStaticActor(pose, geometry, defaultPhysicsMaterial);
		heightFieldGraphicsMesh = createGraphicsMeshFromHeightField(heightField, heightScale, rowScale, columnScale);

		scene->addActor(*heightFieldActor);
	}
	else
	{
		printf("Wczytanie mapy wysokosci nie powiodlo sie.\n");
	}
}

bool SampleShapesGame::loadRawHeightField(const char* filename, unsigned char* buffer, int& width, int& height)
{
	width = height = 0;

	FILE* f = NULL;
	fopen_s(&f, filename, "rb");

	if ( f )
	{
		char signature[16];
		fread(signature, 16, 1, f);

		if ( strcmp("RAW_HEIGHTMAP", signature) == 0 )
		{
			fread(&width, 4, 1, f);
			fread(&height, 4, 1, f);

			GX_ASSERT(width > 0 && height > 0, "Invalid heightmap dimensions.");

			if ( buffer != NULL )
			{
				fread(buffer, width * height, 1, f);
			}
		}

		fclose(f);

		return true;
	}

	return false;
}

void SampleShapesGame::renderHeightFieldActor(PxRigidActor* rigid,Mesh* graphicsMesh, Material* graphicsMaterial)
{
	PxTransform pose = rigid->getGlobalPose();
	PxMat44 mtx = PxMat44(pose);

	effect->setModelMatrix(mtx);
	graphics->renderMesh(effect, graphicsMesh, *graphicsMaterial);
}

void SampleShapesGame::renderTriangleMeshActor(PxRigidActor* rigid, Mesh* graphicsMesh, Material* graphicsMaterial)
{
	PxTransform pose=rigid->getGlobalPose();
	PxMat44 mtx=PxMat44(pose);

	PxShape* shape = NULL;
	rigid->getShapes(&shape,1);
	if(shape!=NULL)
	{
		PxTriangleMeshGeometry geometry=shape->getGeometry().triangleMesh();
		mtx=mtx*MathHelper::createScale(geometry.scale.scale);

		effect->setModelMatrix(mtx);
		graphics->renderMesh(effect,graphicsMesh,*graphicsMaterial);
	}
}

void SampleShapesGame::printHelp()
{
	SampleGame::printHelp();

	std::cout << std::endl << "# Dodawanie ksztaltow" << std::endl
		<< "Spacja - rzucenie pudelkiem w kierunku patrzenia" << std::endl
		<< "Z - dodanie stosu pudelek" << std::endl
		<< "X - dodanie sciany zbudowanej z pudelek" << std::endl
		<< "C - dodanie kapsuly" << std::endl
		<< "V - dodanie sfery" << std::endl;
}

