/*
	OgreNewt library - connecting Ogre and Newton!

	Demo05_SimpleVehicle - how to use the vehicle classes to make a simple vehicle in OgreNewt.
*/

#include "OgreNewtonApplication.h"
#include "OgreNewtonFrameListener.h"

#include <OgreNewt.h>
#include <OgreNewt_BasicFrameListener.h>


OgreNewtonApplication::OgreNewtonApplication(void)
{
	// create OgreNewt world.
	m_World = new OgreNewt::World();

}

OgreNewtonApplication::~OgreNewtonApplication(void)
{
	// destroy world.
	delete m_World;
}


static SimpleVehicle::CAR_CONFIG raceCar = 
{
	"f1Body.mesh",

	// FL_TIRE
	{"f1Tire.mesh", {-0.743107f, -0.023629f, -1.279540f}, {0.70710552f, 0.0f,  0.70710552f, 0.0f}},  

	// FR_TIRE
	{"f1Tire.mesh", { 0.741849f, -0.023629f, -1.279540,}, {0.70710552f, 0.0f, -0.70710552f, 0.0f}},  

	// RR_tire
	{"f1Tire.mesh", { 0.725642f, -0.023618f, 1.717240f}, {0.70710552f, 0.0f, -0.70710552f, 0.0f}},  

	// RL_tire
	{"f1Tire.mesh", {-0.725644f, -0.023618f, 1.717240f}, {0.70710552f, 0.0f,  0.70710552f, 0.0f}},  
};



static SimpleVehicle::CAR_CONFIG Jeep = 
{
	"jeepBody.mesh",

	// FL_TIRE
	{"jeepTire.mesh", {-0.625456f, -0.296697f, -1.235474f}, {0.70710552f, 0.0f, 0.70710552f, 0.0f}},  

	// FR_TIRE
	{"jeepTire.mesh", { 0.625456f, -0.296697f, -1.235474f}, {0.70710552f, 0.0f, 0.70710552f, 0.0f}},  

	// RR_tire
	{"jeepTire.mesh", { 0.625456f, -0.296697f, 0.818093f}, {0.70710552f, 0.0f, 0.70710552f, 0.0f}},  

	// RL_tire
	{"jeepTire.mesh", {-0.625456f, -0.296697f, 0.818093f}, {0.70710552f, 0.0f, 0.70710552f, 0.0f}},  
};

class CarPlacementRaycast: public OgreNewt::BasicRaycast
{
	public:
	CarPlacementRaycast(OgreNewt::Body* car, Ogre::Vector3& location)
		:OgreNewt::BasicRaycast ()
	{
		// ignore go as if will no do the user filter correctly
		Ogre::Vector3 start (location);
		Ogre::Vector3 end (location);
		
		start.y += 100.0f;
		end.y -= 100.0f;

		// re-cast the ray this time filtering the car body
		m_myBody = car;
		go (car->getWorld(), start, end, true);

		OgreNewt::BasicRaycast::BasicRaycastInfo info = getFirstHit();
		m_elevation = 0.5f + start.y + (end.y - start.y) * info.mDistance;
	}

	// skip the car body 
	virtual bool userPreFilterCallback( OgreNewt::Body* body ) 
	{ 
		// ray form casting hi own body
		return (body != m_myBody); 
	}

	Ogre::Real m_elevation;
	OgreNewt::Body* m_myBody;

};


void OgreNewtonApplication::createScene()
{
	// sky box.
	mSceneMgr->setSkyBox(true, "Examples/CloudyNoonSkyBox");
	
	// shadows on!
	mSceneMgr->setShadowTechnique( Ogre::SHADOWTYPE_STENCIL_ADDITIVE );


	// floor object! this time we'll scale it slightly to make it more vehicle-friendly :P
	Ogre::Vector3 size(2.0,0.5,2.0);
	Entity* floor;
	SceneNode* floornode;
	const char *levelName = "track.mesh";
//	const char *levelName = "simple_terrain.mesh";

	floor = mSceneMgr->createEntity("Floor", levelName );
	floornode = mSceneMgr->getRootSceneNode()->createChildSceneNode( "FloorNode" );
	floornode->attachObject( floor );
//	floor->setMaterialName( "Simple/BeachStones" );
	floornode->setScale(size);
	floor->setCastShadows( false );

	char serializeCollisionName[256];
	strcpy (serializeCollisionName, levelName);
	strtok (serializeCollisionName, ".");
	strcat (serializeCollisionName, ".col");

	FILE* file = fopen (serializeCollisionName, "rb");
	if (!file) {
		// serialize the mesh so that next time loads faster
		OgreNewt::CollisionSerializer saveLevelCollision; 
		OgreNewt::CollisionPtr tmpSphape = OgreNewt::CollisionPtr(new OgreNewt::CollisionPrimitives::TreeCollision( m_World, floor, true, 0 ));
		saveLevelCollision.exportCollision(tmpSphape, serializeCollisionName);
		file = fopen (serializeCollisionName, "rb");
	}

	// load level collision from serialized file
	FileHandleDataStream streamFile (file);
	OgreNewt::CollisionSerializer loadLevelCollision; 
	OgreNewt::CollisionPtr col = loadLevelCollision.importCollision(streamFile, m_World);
	fclose (file);
	OgreNewt::Body* bod = new OgreNewt::Body( m_World, OgreNewt::CollisionPtr(col));
	bod->attachNode( floornode );

//	bod->setPositionOrientation( Ogre::Vector3(0.0,-2.0,0.0), Ogre::Quaternion::IDENTITY );

	// set the world bound to the size of the world AABB
	Ogre::AxisAlignedBox worldAABB (bod->getAABB());
	worldAABB.getMinimum().y -= 100.0f;
	worldAABB.getMaximum().y += 100.0f;
	m_World->setWorldSize (worldAABB.getMinimum(), worldAABB.getMaximum());



	// here's where we make the simple vehicle.  everything is taken care of in the constructor.
	mCar = SimpleVehicle::create (&raceCar, mSceneMgr, m_World); 
	// position the car above the ground;
	Ogre::Vector3 location (0.0f, 0.0f, 0.0);
	CarPlacementRaycast castRay (mCar->getBody0(), location);
	location.y = castRay.m_elevation + 0.5f;
	mCar->getBody0()->setPositionOrientation (location, Ogre::Quaternion(Ogre::Quaternion::IDENTITY)); 

	for (int i = 0; i < 4; i ++) {
		for (int j = 0; j < 3; j ++) {
			// position the car above the ground;
			Ogre::Vector3 location (i * 5, 0.0f, -j * 5 - 5);
			mCar = SimpleVehicle::create (&raceCar, mSceneMgr, m_World); 
			CarPlacementRaycast castRay (mCar->getBody0(), location);
			location.y = castRay.m_elevation + 0.5f;
		
			mCar->getBody0()->setPositionOrientation (location, Ogre::Quaternion(Ogre::Quaternion::IDENTITY)); 
		}

		for (int j = 3; j < 6; j ++) {
			// position the car above the ground;
			Ogre::Vector3 location (i * 5, 0.0f, -j * 5 - 5);
			mCar = SimpleVehicle::create (&Jeep, mSceneMgr, m_World); 
			CarPlacementRaycast castRay (mCar->getBody0(), location);
			location.y = castRay.m_elevation + 0.5f;
			mCar->getBody0()->setPositionOrientation (location, Ogre::Quaternion(Ogre::Quaternion::IDENTITY)); 
		}
	}


	// position camera
//	msnCam = mSceneMgr->getRootSceneNode()->createChildSceneNode();
//	msnCam->attachObject( mCamera );

	location.y += 2.0f;
	location.z += 10.0f;
	mCamera->setPosition(location);
//	msnCam->setPosition( 0.0, 1.0, 20.0);

	// set the near and far clip plane
	mCamera->setNearClipDistance(0.2f);
	mCamera->setFarClipDistance(1000.0f);


	//make a light
	Ogre::Light* light;

	light = mSceneMgr->createLight( "Light1" );
	light->setType( Ogre::Light::LT_POINT );
	light->setPosition( Ogre::Vector3(0.0, 100.0, 100.0) );



}


void OgreNewtonApplication::createFrameListener()
{
//	mFrameListener = new OgreNewtonFrameListener( mWindow, mCamera, mSceneMgr, m_World, msnCam, mCar );
	mFrameListener = new OgreNewtonFrameListener( mWindow, mCamera, mSceneMgr, m_World);
	mRoot->addFrameListener(mFrameListener);

	mNewtonListener = new OgreNewt::BasicFrameListener( mWindow, m_World, 100 );
	mRoot->addFrameListener(mNewtonListener);
}

