#include "TestApplication.h"

void createSphereMesh(const std::string& strName, const float r, const int nRings = 16, const int nSegments = 16)
{
	MeshPtr pSphere = MeshManager::getSingleton().createManual(strName, ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
	SubMesh *pSphereVertex = pSphere->createSubMesh();

	pSphere->sharedVertexData = new VertexData();
	VertexData* vertexData = pSphere->sharedVertexData;

	// define the vertex format
	VertexDeclaration* vertexDecl = vertexData->vertexDeclaration;
	size_t currOffset = 0;
	// positions
	vertexDecl->addElement(0, currOffset, VET_FLOAT3, VES_POSITION);
	currOffset += VertexElement::getTypeSize(VET_FLOAT3);
	// normals
	vertexDecl->addElement(0, currOffset, VET_FLOAT3, VES_NORMAL);
	currOffset += VertexElement::getTypeSize(VET_FLOAT3);
	// two dimensional texture coordinates
	vertexDecl->addElement(0, currOffset, VET_FLOAT2, VES_TEXTURE_COORDINATES, 0);
	currOffset += VertexElement::getTypeSize(VET_FLOAT2);

	// allocate the vertex buffer
	vertexData->vertexCount = (nRings + 1) * (nSegments+1);
	HardwareVertexBufferSharedPtr vBuf = HardwareBufferManager::getSingleton().createVertexBuffer(vertexDecl->getVertexSize(0), vertexData->vertexCount, HardwareBuffer::HBU_STATIC_WRITE_ONLY, false);
	VertexBufferBinding* binding = vertexData->vertexBufferBinding;
	binding->setBinding(0, vBuf);
	float* pVertex = static_cast<float*>(vBuf->lock(HardwareBuffer::HBL_DISCARD));

	// allocate index buffer
	pSphereVertex->indexData->indexCount = 6 * nRings * (nSegments + 1);
	pSphereVertex->indexData->indexBuffer = HardwareBufferManager::getSingleton().createIndexBuffer(HardwareIndexBuffer::IT_16BIT, pSphereVertex->indexData->indexCount, HardwareBuffer::HBU_STATIC_WRITE_ONLY, false);
	HardwareIndexBufferSharedPtr iBuf = pSphereVertex->indexData->indexBuffer;
	unsigned short* pIndices = static_cast<unsigned short*>(iBuf->lock(HardwareBuffer::HBL_DISCARD));

	float fDeltaRingAngle = (Math::PI / nRings);
	float fDeltaSegAngle = (2 * Math::PI / nSegments);
	unsigned short wVerticeIndex = 0 ;

	// Generate the group of rings for the sphere
	for( int ring = 0; ring <= nRings; ring++ ) {
		float r0 = r * sinf (ring * fDeltaRingAngle);
		float y0 = r * cosf (ring * fDeltaRingAngle);

		// Generate the group of segments for the current ring
		for(int seg = 0; seg <= nSegments; seg++) {
			float x0 = r0 * sinf(seg * fDeltaSegAngle);
			float z0 = r0 * cosf(seg * fDeltaSegAngle);

			// Add one vertex to the strip which makes up the sphere
			*pVertex++ = x0;
			*pVertex++ = y0;
			*pVertex++ = z0;

			Vector3 vNormal = Vector3(x0, y0, z0).normalisedCopy();
			*pVertex++ = vNormal.x;
			*pVertex++ = vNormal.y;
			*pVertex++ = vNormal.z;

			*pVertex++ = (float) seg / (float) nSegments;
			*pVertex++ = (float) ring / (float) nRings;

			if (ring != nRings) {
                               // each vertex (except the last) has six indices pointing to it
				*pIndices++ = wVerticeIndex + nSegments + 1;
				*pIndices++ = wVerticeIndex;               
				*pIndices++ = wVerticeIndex + nSegments;
				*pIndices++ = wVerticeIndex + nSegments + 1;
				*pIndices++ = wVerticeIndex + 1;
				*pIndices++ = wVerticeIndex;
				wVerticeIndex ++;
			}
		}; // end for seg
	} // end for ring

	// Unlock
	vBuf->unlock();
	iBuf->unlock();
	// Generate face list
	pSphereVertex->useSharedVertices = true;

	// the original code was missing this line:
	pSphere->_setBounds( AxisAlignedBox( Vector3(-r, -r, -r), Vector3(r, r, r) ), false );
	pSphere->_setBoundingSphereRadius(r);
        // this line makes clear the mesh is loaded (avoids memory leaks)
        pSphere->load();
 }



TestApplication::TestApplication(void){
	mAnimState = 0;
	bCount=0;
}

TestApplication::~TestApplication(void){
}

void TestApplication::createFrameListener(){
	//ExampleApplication::createFrameListener();

	MyFrameListener* fl = new MyFrameListener(mWindow,mCamera,1.5 /*Avg walking speed for male in m/s*/);

	fl->mAnimState = mAnimState;
	fl->camBody = cameraBody;

	mFrameListener = fl;//new MyFrameListener(mWindow,mCamera);

	//doesn't work:
	//((MyFrameListener*) &mFrameListener)->mAnimState = mAnimState;

	//mFrameListener= new MyFrameListener( ExampleFrameListener(mWindow, mCamera);
	mFrameListener->showDebugOverlay(false);
	mRoot->addFrameListener(mFrameListener);
}

void TestApplication::createScene(){
	mSceneMgr->setAmbientLight(ColourValue(0.5,0.5,0.5)); //0,0,0 = off
	mSceneMgr->setShadowTechnique(SHADOWTYPE_STENCIL_ADDITIVE);

	Entity* e;
	SceneNode* node;

	float roomWidth = 4;
	float roomHeight = 2.5;
	float roomDepth = 3;

	/*
	e = mSceneMgr->createEntity("NinjaPower","ninja.mesh");
	e->setCastShadows(true);
	mSceneMgr->getRootSceneNode()->createChildSceneNode()->attachObject(e);

	e = mSceneMgr->createEntity("NinjaPower2","ninja.mesh");
	e->setCastShadows(true);
	mSceneMgr->getRootSceneNode()->createChildSceneNode(Vector3(50,0,100))->attachObject(e);

	AnimationState* a;
*/
/*
	AnimationStateSet* s = e->getAllAnimationStates();
	AnimationStateIterator i = s->getAnimationStateIterator();

	

	while(i.hasMoreElements()){
		a = i.getNext();
		LogManager::getSingleton().logMessage("NINJA ANIM: " + a->getAnimationName());
	}
*/
/*
	a = e->getAnimationState("Walk");
	//a = e->getAnimationState("Idle1");
	a->setLoop(true);
	a->setEnabled(true);
	a->addTime(1);

	mAnimState = a;

	//Add a soccer ball
	createSphere("SphereMesh",12);
	e = mSceneMgr->createEntity("soccerBall","SphereMesh");//"sphere.mesh");
	node = mSceneMgr->getRootSceneNode()->createChildSceneNode(Vector3(0,0,-100));
	node->attachObject(e);
	//node->scale(0.5,0.5,0.5);
	node->translate(0,12,0);
	e->setMaterialName("Oz/SoccerBall");
	//e->

*/
	/*
	Plane plane(Vector3::UNIT_Y,0);
	MeshManager::getSingleton().createPlane("my_ground",ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,plane,roomWidth,roomDepth,20,20,true,1,5,5,Vector3::UNIT_Z);
	e = mSceneMgr->createEntity("GroundEntity","my_ground");
	mSceneMgr->getRootSceneNode()->createChildSceneNode()->attachObject(e);
	e->setCastShadows(false);
	//e->setMaterialName("Oz/OfficeFloor1");//Examples/Rockwall");//"Examples/EnvMappedRustySteel")
	e->setMaterialName("Oz/OfficeCeil1");//Examples/Rockwall");//"Examples/EnvMappedRustySteel")

	Plane plane2(Vector3::UNIT_Y,0);
	//MeshManager::getSingleton().createPlane("my_ceil",ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,plane2,1500,1500,20,20,true,1,5,5,Vector3::UNIT_Z);
	e = mSceneMgr->createEntity("CeilEntity","my_ground");
	node = mSceneMgr->getRootSceneNode()->createChildSceneNode();
	node->attachObject(e);
	node->translate(0,roomHeight,0); //2.5 meter high ceiling
	node->roll(Radian(Degree(180)));
	e->setCastShadows(false);
	e->setMaterialName("Oz/OfficeCeil1");//Examples/Rockwall");//"Examples/EnvMappedRustySteel")
	*/

	Light* light = mSceneMgr->createLight("Light1");
	light->setType(Light::LT_POINT);
	light->setPosition(0,roomHeight/2,0);
	light->setDiffuseColour(1.0, 1.0, 1.0);
	light->setSpecularColour(1.0, 1.0, 1.0);
	light->setAttenuation(500,0.5,0.7,0.5);
	
	/*
	//create a sphere to show where the light is
	e = mSceneMgr->createEntity("LightBall","sphere.mesh");
	e->setCastShadows(false);
	e->setMaterialName("drbunsen_head");
	node = mSceneMgr->getRootSceneNode()->createChildSceneNode("LightNode");
	node->scale(.1,.1,.1);
	node->attachObject(e);
	node->setPosition(light->getPosition());
	//
	*/
/*
	light = mSceneMgr->createLight("Light3");
	light->setType(Light::LT_DIRECTIONAL);
	light->setDiffuseColour(ColourValue(.25, .25, 0));
	light->setSpecularColour(ColourValue(.25, .25, 0));

	light->setDirection(Vector3( 0, -1, 1 ));
*/
	/*
	light = mSceneMgr->createLight("Light2");
	light->setType(Light::LT_SPOTLIGHT);
	light->setDiffuseColour(0, 0, 1.0);
	light->setSpecularColour(0, 0, 1.0);

	light->setDirection(-1, -1, 0);
	light->setPosition(Vector3(0, -1, 0));
	light->setSpotlightRange(Degree(35), Degree(50));
	*/

	mWorld = new World("time-controller: ogre");
	mScene = mWorld->createScene("MyScene",mSceneMgr,"gravity: yes, floor: yes, renderer: ogre");
	//mScene->getNxScene()->setsk

	

	//Actor* mActor = mScene->createActor("Spot", new Cube(1),Vector3(0,1,0), "model: cube.mesh, material: Examples/Rockwall, density: 1.0");
	//Body* mBody = mScene->createBody("cube.1m.mesh", new Cube(1*scale,"mesh-scale: 10 10 10"),Vector3(0,10*scale,0),"density: 1.0, material: drbunsen_head, node-scale: 10 10 10");
	Body* mBody;
	
	createSphereMesh("SphereMesh",1);//0.75);

	NxOgre::Material* nxMat;
	nxMat = mScene->createMaterial("SoccerBall");
	nxMat->setAll(0.9,0.6,0.8);
	nxMat->setAll(1,1,1);

	nxMat = mScene->createMaterial("Wall");
	nxMat->setAll(0.2,0.6,0.6);
	nxMat->setAll(1,1,1);

	nxMat = mScene->createMaterial("ConcreteFloor");
	nxMat->setAll(0.2,0.9,0.9);
	nxMat->setAll(1,1,1);

	float wallWidth = 0.3;
	float wallGap = wallWidth/2 * 0.6;

	//construct a room
		//floor
		mBody = createCube(Vector3(roomWidth,wallWidth,roomDepth),"material: ConcreteFloor","Oz/OfficeCeil1",Vector3(0,0-wallGap,0),false);
		//ceil
		mBody = createCube(Vector3(roomWidth,wallWidth,roomDepth),"material: SoccerBall","Oz/OfficeCeil1",Vector3(0,roomHeight+wallGap,0),false);
		//left wall
		mBody = createCube(Vector3(wallWidth,roomHeight,roomDepth),"material: Wall","Oz/OfficeWall1",Vector3(-roomWidth/2-wallGap,roomHeight/2,0),false);
		//right wall
		mBody = createCube(Vector3(wallWidth,roomHeight,roomDepth),"material: Wall","Oz/OfficeWall1",Vector3(roomWidth/2+wallGap,roomHeight/2,0),false);
		//back wall
		mBody = createCube(Vector3(roomWidth,roomHeight,wallWidth),"material: Wall","Oz/OfficeWall1",Vector3(0,roomHeight/2,-roomDepth/2-wallGap),false);
		//front wall
		mBody = createCube(Vector3(roomWidth,roomHeight,wallWidth),"material: Wall","Oz/OfficeWall1",Vector3(0,roomHeight/2,roomDepth/2+wallGap),false);
	//\room

	float scale = 0.11f;

	mBody = createSphere(scale,"linearvelocity: -2 0 -2,angularvelocity: 1 0 1,density: 1, material:SoccerBall","Oz/SoccerBall",Vector3(0,scale,0));

	for( int i = 0;i<100;i++){
		//mBody = createCube(Vector3(scale),"density: 1, material:SoccerBall","Oz/SoccerBall",Vector3(0,scale*1.1*(i+1),0));
		//mBody = createCube(Vector3(scale),"density: 1, material:SoccerBall","Oz/SoccerBall",Vector3(0,scale*1.1*(i+1),0));
		//mBody = createShape("ninja.mesh",new NxOgre::Capsule(0.2,1.5),Vector3(0.005),"density:1","",Vector3(1,0,1),true,true);
		//mBody = createSphere(scale,"linearvelocity:10 -10 0,density: 1, material:SoccerBall","Oz/SoccerBall",Vector3(1,scale*1.1*(i+1)*0.5,scale*i));
		/*
		scale = .3;
		//mBody = mScene->createBody("MyCube; cube.1m.mesh", new Cube(1*scale, "mesh-scale: 2 2 2"),Vector3(0,2 + i*1.5,0),"density: 1.0, node-scale: 2 2 2");
		mBody = mScene->createBody("myBody", new Cube(scale,scale,scale,ShapeParams("skin-width: 0.001")), Vector3(0,scale*1.8*(i+1),0), "model: cube.1m.mesh, scale: .3 .3 .3 material:Examples/Rockwall", "density: 10");
		//mBody->getNxActor()->set

		scale = .11f; //legal soccer ball size ~ 11 cm

		e = mSceneMgr->createEntity("soccerBall" + i,"SphereMesh");//"sphere.mesh");
		node = mSceneMgr->getRootSceneNode()->createChildSceneNode("BallNode" + i);//Vector3(0,0,-100));
		node->attachObject(e);
		node->scale(scale,scale,scale);
		//node->translate(0,12,0);
		e->setMaterialName("Oz/SoccerBall");

		NodeRenderableParams np;
		np.setToDefault();
		np.mIdentifier = "BallNode" + i;
		np.mIdentifierUsage = np.IU_Use;

		ShapeParams sp;
		sp.setToDefault();
		sp.mSkinWidth = 0.0001;
		//sp.mMaterial = "SoccerBall";

		//mBody = mScene->createBody("myBodyC", new NxOgre::Sphere(scale,ShapeParams("skin-width: 0.1")), Vector3(2-i*scale/2,scale*2*(i+1),0), "model: SphereMesh, scale: .3 .3 .3 material:Oz/SoccerBall", "density: 10");
		mBody = mScene->createBody("myBodyC", new NxOgre::Sphere(scale,sp), Vector3(2-i*scale/2,scale*2*(i+1),0),np,"density: 100, material:SoccerBall");
		*/
	}
	//Body* mBody = mScene->createBody("sphere.mesh",new NxOgre::Sphere(1),Vector3(0,10,0),"density:1");

	//cameraBody = createSphere(0.5,"material:SoccerBall","Oz/SoccerBall",Vector3::ZERO,true);
	cameraBody = createShape("",new NxOgre::Sphere(0.5),Vector3::ZERO,"material:SoccerBall","Oz/SoccerBall",Vector3::ZERO,true,true);
	//cameraBody = createCube(Vector3(1,1.8,1),"material:SoccerBall","Oz/SoccerBall",Vector3::ZERO,true);
	cameraBody->raiseBodyFlag(NX_BF_DISABLE_GRAVITY);
	cameraBody->raiseBodyFlag(NX_BF_FROZEN_ROT);
	//cameraBody->raiseBodyFlag(NX_BF_KINEMATIC);
	//cameraBody->setAngularDamping(0);
}

Body* TestApplication::createShape(String mesh, NxOgre::Shape* shape,Vector3 size, ActorParams ap, NxString material, Vector3 offset, bool dynamic, bool castShadows){
	Entity* e;
	SceneNode* node;

	String countStr = StringConverter::toString(bCount);

	String nodeName = "MyNode" + countStr;
	node = mSceneMgr->getRootSceneNode()->createChildSceneNode(nodeName);

	if(mesh!=""){
		String entityName = "MyShape" + countStr;
		e = mSceneMgr->createEntity(entityName,mesh);
		if(material!=""){
			e->setMaterialName(material);
		}
		e->setCastShadows(castShadows);
		node->attachObject(e);
		node->scale(size);
	}
	

	NodeRenderableParams np;
	np.setToDefault();
	np.mIdentifier = nodeName;
	np.mIdentifierUsage = np.IU_Use;

	Body* mBody = mScene->createBody("MyBody", shape,NxOgre::Pose(offset.x,offset.y,offset.z),np,ap);
	
	if(!dynamic){
		mBody->raiseBodyFlag(NX_BF_KINEMATIC);
	}

	bCount++;
	return mBody;
}

Body* TestApplication::createCube(Vector3 size, ActorParams ap, NxString material, Vector3 offset, bool dynamic, bool castShadows){
	return createShape("cube.1m.mesh",new Cube(size,"skin-width:0.0001"),size,ap,material,offset,dynamic,castShadows);
}

Body* TestApplication::createSphere(double radius, ActorParams ap, NxString material, Vector3 offset, bool dynamic, bool castShadows){
	return createShape("SphereMesh",new NxOgre::Sphere(radius,"skin-width:0.0001"),Vector3(radius,radius,radius),ap,material,offset,dynamic,castShadows);
}

void TestApplication::createCamera(){
	mCamera = mSceneMgr->createCamera("PlayerCam");
	mCamera->setFOVy(Radian(Degree(70)));
	mCamera->setPosition(0,1.3,0); //1.8 meter tall person
	mCamera->lookAt(-2,0,-3);
	mCamera->setNearClipDistance(.05);
}

void TestApplication::createViewports(){
	Viewport* vp = mWindow->addViewport(mCamera);
	vp->setBackgroundColour(ColourValue(0,0,0));
	mCamera->setAspectRatio(Real(vp->getActualWidth()/vp->getActualHeight()));
}
/*
void TestApplication::go(){
	if (!setup())
		return;

	
	//mRoot->startRendering();
	

	while(true){
		if(!mRoot->renderOneFrame())
			break;

		mWindow->update();

		WindowEventUtilities::messagePump();
	}

	// clean up
	destroyScene();
}
*/