#include "Application.h"

Application::Application() {
	m_Root = 0;
	m_PFrameListener = 0;
	m_resourcePath = "";
	
}

Application::~Application() {
		delete m_PFrameListener;
		delete m_Root;
}

bool Application::go() {
	createRoot();
	defineResources();   
	if( !setupRenderSystem() ) 
		return false;
	initializeResourceGroups();
	setupScene();
	createFrameListener();
	startRenderLoop();
	return true;
}


void Application::createRoot() {
	//creo l'oggetto root
	String pluginsPath;
	pluginsPath = m_resourcePath + "plugins.cfg";

	m_Root = new Root( pluginsPath, m_resourcePath + "ogre.cfg", m_resourcePath + "Ogre.log" );
}

void Application::defineResources() {
    ConfigFile cf;
    cf.load( m_resourcePath + "resources.cfg" );

    ConfigFile::SectionIterator seci = cf.getSectionIterator();

    String secName, typeName, archName;
    while ( seci.hasMoreElements() ) {
        secName = seci.peekNextKey();
        ConfigFile::SettingsMultiMap *settings = seci.getNext();
        ConfigFile::SettingsMultiMap::iterator i;
        for ( i = settings->begin(); i != settings->end(); ++i ) {
            typeName = i->first;
            archName = i->second;

			ResourceGroupManager::getSingleton().addResourceLocation( archName, typeName, secName );
        }
    }
}

bool Application::setupRenderSystem() {
	//inizializzo il sistema di rendering tramite finestra di ogre
	if( m_Root->showConfigDialog() ) {
        m_RenderWindow = m_Root->initialise( true );
		return true;
	} else
		return false;
}

void Application::initializeResourceGroups() {
	//configuro il numero di default di mipmap per le texture
	TextureManager::getSingleton().setDefaultNumMipmaps( 5 );
	//inizializzo le risorse
	ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
}

void Application::setupScene() {
	//creo lo scene manager
	m_SceneManager = m_Root->createSceneManager( ST_EXTERIOR_REAL_FAR, "Project SceneManager" );
	//creo la camera e la posiziono
	m_Camera = m_SceneManager->createCamera( "Camera" );
	m_Camera->setNearClipDistance(2);
	//creo la viewport
	Viewport* vp = m_RenderWindow->addViewport( m_Camera );
	// Modifico l'aspect ratio della camera in modo che combaci con quello della viewport
	m_Camera->setAspectRatio( Real( vp->getActualWidth() ) / Real( vp->getActualHeight() ) );

	m_Overlay = OverlayManager::getSingleton().getByName("Asteroid_Cargo_Overlay");   
	OverlayManager::getSingleton().getOverlayElement("MalusPanel")->setCaption("");
	OverlayManager::getSingleton().getOverlayElement("MalusTimer")->setCaption("");
	
	m_OverlayTimer = OverlayManager::getSingleton().getByName("Asteroid_Cargo_Timer");
	OverlayManager::getSingleton().getOverlayElement("Timer")->setCaption("");
	m_Overlay->show();
	m_OverlayTimer->show();
	
	m_SceneManager->setAmbientLight(ColourValue(0.8, 0.8, 0.8));
	//m_SceneManager->setShadowTechnique(SHADOWTYPE_STENCIL_ADDITIVE);

	Light* l = m_SceneManager->createLight("MainLight");
	l->setType(Light::LT_DIRECTIONAL);
	l->setDirection(-0.5, -0.5, 0);

	m_Camera->setPosition(0, 0, 0);

	// creo l'anello gigante che mi darà l'orizzonte
	SceneNode * AnelloNode;
	Entity * AnelloEntity;

	AnelloNode = m_SceneManager->getRootSceneNode()->createChildSceneNode( "AnelloNode" );
	AnelloEntity = m_SceneManager->createEntity( "AnelloEntity", "anello.mesh" );
	AnelloEntity->setMaterialName("phong1");
	AnelloNode->attachObject( AnelloEntity );
	AnelloNode->scale( 10000, 5000, 10000);

	int MalusQuantity = 5;

	// Popolo la zona in modo random con teste di ogre
	// verranno sostituite con le casse
	SceneNode * CassaNode;
	Entity * CassaEntity;
	for ( int i = 0; i < 30; ++i ) {
		CassaNode = m_SceneManager->getRootSceneNode()->createChildSceneNode( "cassa" + StringConverter::toString( i ), Vector3 ( Math::RangeRandom( -5000, 5000 ), Math::RangeRandom( -5000, 5000 ), Math::RangeRandom( -5000, 5000 ) ) );
		CassaEntity = m_SceneManager->createEntity( StringConverter::toString(i), "cassa.mesh" );
		CassaEntity->setMaterialName( "cassa/cassa" );
		CassaEntity->setQueryFlags( MOVABLE_OBJECT );
		CassaNode->attachObject( CassaEntity );
		CassaNode->scale( Vector3 ( 100, 100, 100 ) );
	}
	//Sequenza di creazione dei malus
	for ( int i = 0; i < MalusQuantity; ++i ) {
		CassaNode = m_SceneManager->getRootSceneNode()->createChildSceneNode( "malus_turbo" + StringConverter::toString( i ), Vector3 ( Math::RangeRandom( -5000, 5000 ), Math::RangeRandom( -5000, 5000 ), Math::RangeRandom( -5000, 5000 ) ) );
		CassaEntity = m_SceneManager->createEntity( "Ent_malus_turbo" + StringConverter::toString(i), "cassa.mesh" );
		CassaEntity->setMaterialName( "cassa/cassa" );
		CassaEntity->setQueryFlags( TURBO_MALUS );
		CassaNode->attachObject( CassaEntity );
		CassaNode->scale( Vector3 ( 100, 100, 100 ) );
	}

	for ( int i = 0; i < MalusQuantity; ++i ) {
		CassaNode = m_SceneManager->getRootSceneNode()->createChildSceneNode( "malus_slowmotion" + StringConverter::toString( i ), Vector3 ( Math::RangeRandom( -5000, 5000 ), Math::RangeRandom( -5000, 5000 ), Math::RangeRandom( -5000, 5000 ) ) );
		CassaEntity = m_SceneManager->createEntity( "Ent_malus_slowmotion" + StringConverter::toString(i), "cassa.mesh" );
		CassaEntity->setMaterialName( "cassa/cassa" );
		CassaEntity->setQueryFlags( SLOWMOTION_MALUS );
		CassaNode->attachObject( CassaEntity );
		CassaNode->scale( Vector3 ( 100, 100, 100 ) );
	}

	for ( int i = 0; i < MalusQuantity; ++i ) {
		CassaNode = m_SceneManager->getRootSceneNode()->createChildSceneNode( "malus_pig" + StringConverter::toString( i ), Vector3 ( Math::RangeRandom( -5000, 5000 ), Math::RangeRandom( -5000, 5000 ), Math::RangeRandom( -5000, 5000 ) ) );
		CassaEntity = m_SceneManager->createEntity( "Ent_malus_pig" + StringConverter::toString(i), "cassa.mesh" );
		CassaEntity->setMaterialName( "cassa/cassa" );
		CassaEntity->setQueryFlags( PIG_MALUS );
		CassaNode->attachObject( CassaEntity );
		CassaNode->scale( Vector3 ( 100, 100, 100 ) );
	}

	for ( int i = 0; i < MalusQuantity; ++i ) {
		CassaNode = m_SceneManager->getRootSceneNode()->createChildSceneNode( "malus_inverted" + StringConverter::toString( i ), Vector3 ( Math::RangeRandom( -5000, 5000 ), Math::RangeRandom( -5000, 5000 ), Math::RangeRandom( -5000, 5000 ) ) );
		CassaEntity = m_SceneManager->createEntity( "Ent_malus_inverted" + StringConverter::toString(i), "cassa.mesh" );
		CassaEntity->setMaterialName( "cassa/cassa" );
		CassaEntity->setQueryFlags( INVERTED_MALUS );
		CassaNode->attachObject( CassaEntity );
		CassaNode->scale( Vector3 ( 100, 100, 100 ) );
	}

	for ( int i = 0; i < MalusQuantity; ++i ) {
		CassaNode = m_SceneManager->getRootSceneNode()->createChildSceneNode( "malus_invisible" + StringConverter::toString( i ), Vector3 ( Math::RangeRandom( -5000, 5000 ), Math::RangeRandom( -5000, 5000 ), Math::RangeRandom( -5000, 5000 ) ) );
		CassaEntity = m_SceneManager->createEntity( "Ent_malus_invisible" + StringConverter::toString(i), "cassa.mesh" );
		CassaEntity->setMaterialName( "cassa/cassa" );
		CassaEntity->setQueryFlags( INVISIBLE_MALUS );
		CassaNode->attachObject( CassaEntity );
		CassaNode->scale( Vector3 ( 100, 100, 100 ) );
	}




	
	// popolo la scena con gli oggetti missione
	m_Stargate = m_SceneManager->getRootSceneNode()->createChildSceneNode( "Stargate", Vector3::ZERO );
	SceneNode * Mission_Node= m_Stargate->createChildSceneNode( "Nodo_Missione", Vector3::ZERO );
	SceneNode * Sphere_Node;
	Entity * Sphere_Entity;
	double a = 0;
	float x = 0, y = 0;
	int helpLimit = 12;
	
	for ( int i = 0; i < 24; ++i ) {
		a = i * 14.4;		
		x = Math::Cos( Radian( a ) );
		y = Math::Sin( Radian( a ) );
		Sphere_Node = Mission_Node->createChildSceneNode( StringConverter::toString( i ), Vector3 ( Real( x*1000 ), Real( y*1000 ), 0 ) );
		Sphere_Entity = m_SceneManager->createEntity( "SphereEnt" + StringConverter::toString ( i ), "redsphere.mesh" );

		//faccio in modo che alcune sfere siano già attivate
		if( ( helpLimit > 0) && ( Math::RangeRandom( -5, 5 ) > 0 ) ) {
			Sphere_Entity->setMaterialName( "green" );
			m_MissionObjects[i] = false;
			helpLimit -= 1;
		} else {
			Sphere_Entity->setQueryFlags( MISSION_OBJECT );
			m_MissionObjects[i] = true;
		}
		Sphere_Node->attachObject( Sphere_Entity );
		Sphere_Node->scale( 100, 100, 100);
	}

	// creo il satellite
	Entity * StarEntity = m_SceneManager->createEntity("stargate2", "stargate2.mesh");
	StarEntity->setMaterialName( "phong1" );
	m_StarMeshNode = m_Stargate->createChildSceneNode(Vector3::ZERO);
	m_StarMeshNode->attachObject( StarEntity );
	m_StarMeshNode->scale( 1100, 1100, 1100);
	m_StarMeshNode->rotate( Vector3(0, 1, 0), Radian( Degree(90)) );

	StarEntity = m_SceneManager->createEntity("stargate1", "stargate1.mesh");
	StarEntity->setMaterialName( "phong1" );
	m_StarMeshNode = m_Stargate->createChildSceneNode(Vector3::ZERO);
	m_StarMeshNode->attachObject( StarEntity );
	m_StarMeshNode->scale( 1100, 1100, 1100);
	m_StarMeshNode->rotate( Vector3(0, 1, 0), Radian( Degree(90)) );

	StarEntity = m_SceneManager->createEntity("portale", "portale.mesh");
	StarEntity->setMaterialName( "Examples/TextureEffect3" );
	m_StarMeshNode = m_Stargate->createChildSceneNode( "NodoPortale", Vector3::ZERO);
	m_StarMeshNode->attachObject( StarEntity );
	m_StarMeshNode->scale( 1100, 1100, 1100);
	m_StarMeshNode->rotate( Vector3(0, 1, 0), Radian( Degree(90)) );
	m_StarMeshNode->setVisible( false);




	// Creo la Navicella
	m_Navi = new Navicella ( "Navicella", m_SceneManager, m_Overlay, m_MissionObjects );
	m_ExCamera = new ExtendedCamera ( "Extended Camera", m_SceneManager, m_Camera );

	SceneNode * m_BgNode;
	Entity * m_BgEntity = m_SceneManager->createEntity( "background", "background.mesh" );
	m_BgNode  = m_SceneManager->getRootSceneNode()->createChildSceneNode( Vector3::ZERO );
	m_BgNode->attachObject( m_BgEntity );
	m_BgNode->scale( 60000, 60000, 60000 );


	//creo un nodo a cui attaccare la camera alla fine del gioco
	SceneNode * m_CameraAnimationNode = m_SceneManager->getRootSceneNode()->createChildSceneNode( "CameraFinale", Vector3( -600, 800, -1300 ) );

	//creo l'animazione finale e imposto la sua durata in secondi
	Animation * m_Animation = m_SceneManager->createAnimation( "FinePartita", 10 );

	//imposto la modalità di interpolazione dei keyframe
	m_Animation->setInterpolationMode( Animation::IM_LINEAR );

	//creo un nodo su cui deve agire l'animazione
	SceneNode * m_AnimationNode = m_SceneManager->getRootSceneNode()->createChildSceneNode( "NodoAnimazioneFinale", Vector3( 0, 0, 0 ) );
	
	//creo una traccia di animazione che lavori su uno scenenode
	NodeAnimationTrack * m_AnimationTrack = m_Animation->createNodeTrack( 0, m_AnimationNode );

	//creo un keyframe
	TransformKeyFrame * m_Key;
	//definisco i valori del keyframe
	m_Key = m_AnimationTrack->createNodeKeyFrame( 0 );
	m_Key->setTranslate( Vector3( 0, 0, -2000 ) );

	m_Key = m_AnimationTrack->createNodeKeyFrame( 10 );
	m_Key->setTranslate( Vector3( 0, 0, 2000 ) );

	//creo un animation state che si occupa dell'aggiornamento del nodo animato
	m_AnimationState = m_SceneManager->createAnimationState( "FinePartita" );


}

void Application::createFrameListener() {
	//creo un nuovo frame listener
	m_PFrameListener = new PFrameListener( m_RenderWindow, m_Camera, m_SceneManager );
	static_cast<PFrameListener *>( m_PFrameListener )->setSceneObject( m_Navi );
	static_cast<PFrameListener *>( m_PFrameListener )->setExtendedCamera( m_ExCamera );
	m_Root->addFrameListener( m_PFrameListener );
}

void Application::startRenderLoop() {
	m_Root->startRendering();
}