//********************************************************************
// This source file is part of Ninfa3D Engine
// 
// Copyright (C) 2008-2009 Viz_fuerte.
// All rights reserved.
// 
// LIMITED WARRANTY
// This software is provided "as-is", without any express or implied warranty.
// In no event shall the author be held liable for any damages arising from the use of this software.
// This is a contract. By accepting the Ninfa3D Engine (the SOFTWARE), 
// you (either an individual or a single entity, the LICENSEE) accept the terms and conditions of this licensing agreement.
// 
// If the LICENSEE does not accept the terms and conditions, then LICENSEE must not accept the SOFTWARE.
// If at a later time the LICENSEE decides not to continue licensing the SOFTWARE under these terms and 
// conditions, then the LICENSEE must delete permanently all files constituting the SOFTWARE.
//
// File: Global.cpp
//********************************************************************

/*--------------------------------------------------------------------
Includos*/

	#include "..\..\Header Files\Ninfa3D.h"
	#include "..\..\Codes\CMastEventReceiver.h"

/*--------------------------------------------------------------------
Variables externas*/

	extern array<NewtonBody*> gBody;
	extern int TotalMeshes;

/*--------------------------------------------------------------------
Variables globales*/

	// Globales para Irrlicht
	IrrlichtDevice* device;
	IVideoDriver* driver;
	ISceneManager* SceneManager;
	IGUIEnvironment* guienv;
	IGPUProgrammingServices* gpu;
	IFileSystem* filesy;
	IGUIFont* font;
	IGUIInOutFader* fader;
	MastEventReceiver eventReceiver;

	// Globales para Newton
	int defaultMaterialID;
	NewtonWorld *nWorld;

	// Globales para OpenAL
	ALCdevice* OALDevice;
	ALCcontext* OALContext;

	unsigned int bg_red = 0;
	unsigned int bg_green = 0;
	unsigned int bg_blue = 0;

	bool Antialias = false;
	bool Vsync = false;

	u32 starttime = 0;
	u32 endtime = 0;
	u32 lockto = 0; //Locks to 30 FPS; denominator = wanted FPS 

/*--------------------------------------------------------------------
Declaracion global de funciones*/

extern "C"
{

	void* _cdecl PhysicsAlloc(int sizeInBytes);
	void _cdecl PhysicsFree(void *ptr,int sizeInBytes);

	/*====================================================================
	Inicia el motor*/

	DLL_EXPORT void DLL_APIENTRY InitEngine(int X,int Y,int Bits,bool Fullscreen)
	{
		eventReceiver.init();

		cout << "---------------------------------------------------------"  << endl;
		cout << "Ninfa3D Engine Lite - original -By VIZ.fuerte-" << endl;
		cout << "---------------------------------------------------------"  << endl;
		cout << "Ninfa3D Engine v1.0g adapted by nespa - January 2010 "<< endl;
		cout << "(Irrlicht 1.6.1 & Newton 2.16)" << endl;
		cout << "---------------------------------------------------------"  << endl;
		// Irrlicht
		SIrrlichtCreationParameters param;
		param.DriverType    = EDT_OPENGL;
		param.WindowSize    = dimension2d<s32>(X,Y);
		param.Bits		    = Bits;
		param.Fullscreen    = Fullscreen;
		param.AntiAlias     = Antialias;
		param.Stencilbuffer = true;
		param.Vsync		    = Vsync;
		param.EventReceiver = &eventReceiver;

		device = createDeviceEx(param);

		cout << "Using sound: OpenAL version 1.1" << endl;
		cout << "Using physic: Newton 2.16 Archemedia"  << endl;
		cout << "" << endl;
		cout << "---------------------------------------------------------"  << endl;
		cout << "" << endl;


		driver =       device->getVideoDriver();
		SceneManager = device->getSceneManager();
		guienv =       device->getGUIEnvironment();
		gpu    =       driver->getGPUProgrammingServices();
		filesy =       device->getFileSystem();

		font = device->getGUIEnvironment()->getBuiltInFont();
		fader = guienv->addInOutFader();

		if(Bits==32)
		{
			driver->setTextureCreationFlag(ETCF_ALWAYS_16_BIT,false);
			driver->setTextureCreationFlag(ETCF_ALWAYS_32_BIT,true);
		}
		else
		{
			driver->setTextureCreationFlag(ETCF_ALWAYS_16_BIT,true);
			driver->setTextureCreationFlag(ETCF_ALWAYS_32_BIT,false);
		}

		driver->setTextureCreationFlag(ETCF_OPTIMIZED_FOR_QUALITY,true);

		// OpenAL
		OALDevice  = alcOpenDevice(NULL);
		OALContext = alcCreateContext(OALDevice,NULL);
			alcMakeContextCurrent(OALContext);
			alGetError();

		// Create Newton world
		//nWorld = NewtonCreate(PhysicsAlloc,PhysicsFree);
		nWorld = NewtonCreate();

		defaultMaterialID = NewtonMaterialGetDefaultGroupID(nWorld);
		NewtonMaterialSetDefaultSoftness (nWorld,defaultMaterialID,defaultMaterialID, 0.05f);
		NewtonMaterialSetDefaultElasticity (nWorld,defaultMaterialID,defaultMaterialID, 0.4f);
		NewtonMaterialSetDefaultCollidable (nWorld,defaultMaterialID,defaultMaterialID, 1);
		NewtonMaterialSetDefaultFriction (nWorld,defaultMaterialID,defaultMaterialID, 1.0f, 0.5f);

		dVector minWorldBox(-512.0f,-512.0f,-512.0f);
		dVector maxWorldBox( 512.0f, 512.0f, 512.0f);
		NewtonSetWorldSize(nWorld,&minWorldBox.m_x,&maxWorldBox.m_x);
	}

	/*====================================================================
	Devuelve los frames por segundo*/

	//FramesPerSecond
	//float fpsindex,fpstime;
	//double fpsfps;
	//clock_t fpsfold_millisecs;

	DLL_EXPORT void DLL_APIENTRY FrameRateLimit(unsigned int limit)
	{
		lockto = 1000 / limit;
	}

	DLL_EXPORT float DLL_APIENTRY Fps()
	{
		return driver->getFPS();
	}

	/*====================================================================
	Devuelve el numero de triangulos de la escena*/

	DLL_EXPORT unsigned int DLL_APIENTRY TrisRendered()
	{
		return driver->getPrimitiveCountDrawn();
	}

	/*====================================================================
	INICIA LA ESCENA*/

	DLL_EXPORT void DLL_APIENTRY BeginScene()
	{
		eventReceiver.endEventProcess();
		driver->beginScene(true,true,SColor(255,bg_red,bg_green,bg_blue));
		starttime = device->getTimer()->getTime(); 
	}

	/*====================================================================
	ACTUALIZA EL MOTOR*/

	DLL_EXPORT void DLL_APIENTRY UpdateEngine(float timeStep,int mode)
	{
		if(mode == 0 || mode == 3)
		{
		ALfloat x,y,z;
		ALfloat ori[6];

		ICameraSceneNode* cam = SceneManager->getActiveCamera();

		x = cam->getPosition().X;
		y = cam->getPosition().Y;
		z = cam->getPosition().Z;

		ori[0] = cam->getTarget().X - cam->getPosition().X;
		ori[1] = cam->getTarget().Y - cam->getPosition().Y;
		ori[2] = -(cam->getTarget().Z - cam->getPosition().Z);
		ori[3] = cam->getUpVector().X;
		ori[4] = cam->getUpVector().Y;
		ori[5] = -(cam->getUpVector().Z);

		alListener3f(AL_POSITION,x,y,-z);
		alListenerfv(AL_ORIENTATION,ori);
		}

		if(mode == 0 || mode == 1)
		{
			SceneManager->drawAll();
			guienv->drawAll();
		}

		if(mode == 0 || mode == 2)
		{
			NewtonUpdate(nWorld,timeStep);
		}

	}

	/*====================================================================
	FINALIZA LA ESCENA*/

	DLL_EXPORT void DLL_APIENTRY EndScene()
	{
		endtime = device->getTimer()->getTime();

		if(lockto>0)
		{
			if((endtime-starttime) < lockto)
				device->sleep(lockto-(endtime-starttime)); 
		}

		driver->endScene();
		eventReceiver.startEventProcess();
	}

	/*====================================================================
	FINALIZA EL MOTOR*/

	DLL_EXPORT void DLL_APIENTRY EndEngine()
	{
		// OpenAL
		alcMakeContextCurrent(NULL);
		alcDestroyContext(OALContext);
		alcCloseDevice(OALDevice);
		alutExit();

		// Newton
		NewtonDestroyAllBodies(nWorld);
		NewtonMaterialDestroyAllGroupID(nWorld);
		NewtonDestroy(nWorld);

		// Irrlicht
		driver->removeAllHardwareBuffers();
		driver->removeAllTextures();
		device->drop();
	}

	/*====================================================================
	Devuelve TRUE si el motor esta encendido*/

	DLL_EXPORT bool DLL_APIENTRY EngineRun()
	{
		return device->run();
	}

	/*====================================================================
	Activa el anti-alisado*/

	DLL_EXPORT void DLL_APIENTRY EnableAntialias(bool Level)
	{
		Antialias = Level;
	}

	/*====================================================================
	Activa la sincronizacion vertical*/

	DLL_EXPORT void DLL_APIENTRY EnableVsync()
	{
		Vsync = true;
	}

	/*====================================================================
	Cambia la luz ambiental*/

	DLL_EXPORT void DLL_APIENTRY AmbientLight(unsigned int R,unsigned int G,unsigned int B)
	{
		SceneManager->setAmbientLight(SColorf((float(R)/255),(float(G)/255),(float(B)/255),1.0f));
	}

	/*====================================================================
	Cambia el color de la sombra*/

	DLL_EXPORT void DLL_APIENTRY ShadowColor(unsigned int R,unsigned int G,unsigned int B,unsigned int A)
	{
		SceneManager->setShadowColor(SColor(A,R,G,B));
	}

	/*====================================================================
	Cambia el color del fondo*/

	DLL_EXPORT void DLL_APIENTRY BackGroundColor(unsigned int R,unsigned int G,unsigned int B)
	{
		bg_red = R;
		bg_green = G;
		bg_blue = B;
	}

	/*====================================================================
	ACTIVA LA NIEBLA.*/

	DLL_EXPORT void DLL_APIENTRY Fog(unsigned int R,unsigned int G,unsigned int B,float Density)
	{
		driver->setFog(SColor(255,R,G,B),EFT_FOG_EXP2,0,0,Density,true,false);
	}

	/*====================================================================
	CIERRA EL MOTOR.*/

	DLL_EXPORT void DLL_APIENTRY CloseEngine()
	{
		device->closeDevice();
	}

	/*====================================================================
	HACE QUE EL MOTOR SE PARE.*/

	DLL_EXPORT void DLL_APIENTRY EngineSleep(unsigned int TimeMs)
	{
		device->sleep(TimeMs,true);
	}

	/*====================================================================
	Crea la colision entre dos cuerpos*/

	DLL_EXPORT void DLL_APIENTRY Collisions(IAnimatedMeshSceneNode* mesh,IAnimatedMeshSceneNode* entity,int mode,
											float SX,float SY,float SZ,
											float GX,float GY,float GZ,
											float OFFX,float OFFY,float OFFZ)
	
	{  
		IMeshManipulator* meshManipulator = SceneManager->getMeshManipulator();

		ITriangleSelector* selector;

		switch(mode)
		{
		case(0):
			selector = SceneManager->createTriangleSelectorFromBoundingBox(mesh); 
			break;
		case(1):
			selector = SceneManager->createTriangleSelector(mesh->getMesh(),mesh); 
			break;
		case(2):
			selector = SceneManager->createOctTreeTriangleSelector(mesh->getMesh(),mesh,meshManipulator->getPolyCount(mesh->getMesh()));
			break;
		case(3):
			selector = SceneManager->createTerrainTriangleSelector((ITerrainSceneNode*)mesh); 
			break;
		
		}

		

		mesh->setTriangleSelector(selector);

		
		// create collision response using all objects for collision detection

  		scene::ISceneNodeAnimator* anim =
   		SceneManager->createCollisionResponseAnimator(selector, entity, core::vector3df(SX,SY,SZ), core::vector3df(GX,GY,GZ), core::vector3df(OFFX,OFFY,OFFZ));
    	entity->addAnimator(anim);

	}

	/*====================================================================
	HACE UNA CAPTURA DE LA PANTALLA Y LA GUARDA EN UN ARCHIVO.*/

	DLL_EXPORT void DLL_APIENTRY ScreenShot()
	{
		driver->writeImageToFile(driver->createScreenShot(),"screenshot.jpg");
	}

	/*====================================================================
	Devuelve el numero total de mallas que tiene la escena*/

	DLL_EXPORT int DLL_APIENTRY PrimitiveCount()
	{
		return TotalMeshes;
	}

	/*====================================================================
	Devuelve el numero total de cuerpos que tiene la escena*/

	DLL_EXPORT int DLL_APIENTRY BodyCount()
	{
		return gBody.size();
	}

	/*====================================================================
	Devuelve el numero total de luces que hay en la escena*/

	DLL_EXPORT unsigned int DLL_APIENTRY LightCount()
	{
		return driver->getDynamicLightCount();
	}

	// memory allocation for Newton
	void* _cdecl PhysicsAlloc(int sizeInBytes)
	{
		return malloc(sizeInBytes);
	}

	// memory de-allocation for Newton
	void _cdecl PhysicsFree(void *ptr,int sizeInBytes)
	{
		free(ptr);
	}

}