#include "MapViewerApp.h"
extern KODIAK_DEBUG_STREAM KODIAK_DEBUG_COUT;


void MapViewerApp::mouseWheelEvent( int delta )
{
	this->mouse.wheel = delta;
}

void MapViewerApp::setResolution( unsigned int sizeX, unsigned int sizeY )
{
	this->windowRes[0] = sizeX;
	this->windowRes[1] = sizeY;

	if ( this->render!= NULL )
	{
		this->render->setViewport(0,0, this->windowRes[0], this->windowRes[1] );

		this->render->setProjection( float( this->windowRes[0] ) / float ( this->windowRes[1] ), 
									this->sharedData.externalParams.projection.zNear, this->sharedData.externalParams.projection.zFar,
									this->sharedData.externalParams.projection.FOV );
	}
}

void MapViewerApp::mouseMoveEvent( unsigned int posX, unsigned int posY )
{
	this->mouse.setPos( posX, posY );

	if ( this->mouse.pressedButtons[ Mouse::RIGHT ] )
	{
		QCursor cursor = this->myWidget->cursor();
		QPoint widgetGlobalPos = this->myWidget->mapToGlobal( this->myWidget->pos() );
		cursor.setPos(  widgetGlobalPos.x() + this->myWidget->width() / 2, widgetGlobalPos.y()  + this->myWidget->height() / 2 );
	}
}

void MapViewerApp::mouseReleaseEvent( unsigned int mouseButton )
{
	this->newClick = false;
	this->mouse.pressedButtons[ mouseButton ] = false;

	if ( mouseButton == Mouse::RIGHT )
	{
		//set parent cursor to restore the previous cursor
		this->myWidget->setCursor( this->myWidget->parentWidget()->cursor() );
	}
}

void MapViewerApp::mousePressEvent( unsigned int mouseButton )
{
	this->mouse.pressedButtons[ mouseButton ] = true;

	if ( mouseButton == Mouse::RIGHT )
	{
		//hide cursor while cursor is centered
		this->myWidget->setCursor( QCursor( Qt::BlankCursor ) );

		if ( this->newClick == false  )	
		{
			this->newClick = true;
			this->mouse.setPos( this->myWidget->width()/2, this->myWidget->height()/2 );
			this->mouse.clearDiff();
		}
	

		QCursor cursor = this->myWidget->cursor();
		QPoint widgetGlobalPos = this->myWidget->mapToGlobal( this->myWidget->pos() );
		cursor.setPos(  widgetGlobalPos.x() + this->myWidget->width() / 2, widgetGlobalPos.y() + this->myWidget->height() / 2 );
	}
}

void MapViewerApp::keyPressEvent(unsigned int key)
{
	this->keyboard.pressKey( key );
}

void MapViewerApp::keyReleaseEvent(unsigned int key)
{
	this->keyboard.releaseKey( key );
}

void MapViewerApp::onOpenGLInit(void)	//init is called from GLWidget!!!
{
	this->sharedData.init();

	CGprogram vs, fs;
	
	if ( this->sharedData.shaderManager != NULL )
	{
		vs = this->sharedData.shaderManager->getShader("GLphongLightingVs");
		fs = this->sharedData.shaderManager->getShader("GLphongLightingFs");
	}
	else
	{
		KODIAK_WARNING<<"CgShaderManager is not initialized!";
	}

	this->render = new SoftProjRender( *this->context, vs, fs);
	cout<<endl<<" INIT::res"<<this->windowRes[0]<<", "<<this->windowRes[1]<<endl;

	this->render->setViewport(0,0, this->windowRes[0], this->windowRes[1] );
	this->render->setProjection( float( this->windowRes[0] ) / float ( this->windowRes[1] ), this->sharedData.externalParams.projection.zNear, this->sharedData.externalParams.projection.zFar, this->sharedData.externalParams.projection.FOV );
	

	//-----------------------------

	this->animationManager = new AnimationManager();
	//-----load explosion animation-------------
	Texture<unsigned char> texData;
	float animationTimeOffset = 1.0;
	vector< AnimationFrame> animation;
	for ( int i=0; i<16; ++i)
	{
		TextureManager::getInstance().readTexture<unsigned char>( this->sharedData.externalParams.texturesPath+"exp0_"+intToString(i)+".png",texData);
		unsigned int texId = TextureManager::getInstance().createTexture("explosion0_"+intToString(i), texData);

		AnimationFrame frame;
		frame.textureId = texId;
		frame.time = i * animationTimeOffset;
		animation.push_back( frame );
	}
	//-------------------------------------------

	this->animationManager->insertAnimation( "explosion1", animation  );
	AnimationEffect explosionAnimation( *this->animationManager->getAnimation( "explosion1" ), animation.size() * animationTimeOffset );
	explosionAnimation.pos = MAKE_POINT<float>(-10,0,0);
	explosionAnimation.size[0] = 10;
	explosionAnimation.size[1] = 10;

	//--------------------------------------------

	//-----load warp animation-------------

	animationTimeOffset = 0.25;
	animation.clear();
	for ( int i=0; i<29; ++i)
	{
		TextureManager::getInstance().readTexture<unsigned char>( this->sharedData.externalParams.texturesPath+"warp"+intToString(i)+".png",texData);
		unsigned int texId = TextureManager::getInstance().createTexture("warp_"+intToString(i), texData);

		AnimationFrame frame;
		frame.textureId = texId;
		frame.time = i * animationTimeOffset;
		animation.push_back( frame );
	}

	this->animationManager->insertAnimation( "warp", animation );
	AnimationEffect warpAnimation( *this->animationManager->getAnimation( "warp" ), animation.size() * animationTimeOffset );
	warpAnimation.pos = MAKE_POINT<float>(-10,0,0);
	warpAnimation.size[0] = 40;
	warpAnimation.size[1] = 40;
	//-------------------------------------------

	//--------------------------------------------
	this->spaceMap = new SpaceMapGame( *this->render, explosionAnimation, warpAnimation );
	this->spaceMap->setBackground( TextureManager::getInstance().getTexture( "skyPlane" ) );
	this->spaceMap->setEmittorTex( type0, TextureManager::getInstance().getTexture("particle0") );
	this->spaceMap->setEmittorTex( type1, TextureManager::getInstance().getTexture("particle0") );
	this->spaceMap->setEmittorTex( type2, TextureManager::getInstance().getTexture("particle0") );
	this->spaceMap->setEmittorTex( type3, TextureManager::getInstance().getTexture("particle0") );
	this->spaceMap->setEmittorTex( type4, TextureManager::getInstance().getTexture("particle0") );
	this->spaceMap->setEmittorTex( type5, TextureManager::getInstance().getTexture("particle0") );

	//TODO: nehezke, rozsirit typy lodi asi...
	const unsigned int MINING_MACHINE = 6;
	this->spaceMap->setEmittorTex( (ShipType) MINING_MACHINE , TextureManager::getInstance().getTexture("particle0") );

	this->spaceMap->mapFunctionality.noisyScreenEffect.texId =  TextureManager::getInstance().getTexture("brokenGlass");
	this->spaceMap->mapFunctionality.noisyScreenEffect.leftDownPos[0] = -100;
	this->spaceMap->mapFunctionality.noisyScreenEffect.leftDownPos[1] = -100;
	this->spaceMap->mapFunctionality.noisyScreenEffect.jitteringEffect[0] = 10;
	this->spaceMap->mapFunctionality.noisyScreenEffect.jitteringEffect[1] = 10;
	this->spaceMap->mapFunctionality.noisyScreenEffect.size[0] = 1024;
	this->spaceMap->mapFunctionality.noisyScreenEffect.size[1] = 1024;
	this->spaceMap->mapFunctionality.noisyScreenEffect.alphaOffset = 0.01;
	
	this->spaceMap->mapFunctionality.fogTextureId = TextureManager::getInstance().getTexture("fog");
	this->spaceMap->mapFunctionality.discTextureId = TextureManager::getInstance().getTexture("disc");
	this->spaceMap->mapFunctionality.hintTextureId = TextureManager::getInstance().getTexture("quickKey");
	



	//load missiles models
	for ( int i=0; i< missilesTypeCount; ++i)
	{
		//missiles models are stored in model database under string 'missile'+[number]
		const string MISSILE_MODEL("missile");

		std::map<string, Model* >::iterator modelIt = this->sharedData.modelManager.find( MISSILE_MODEL+intToString( i ) );
		if ( modelIt == this->sharedData.modelManager.end() )
		{
			throw exception( (string("Model with name: ")+ MISSILE_MODEL +intToString( i ) + string(" was not found")).c_str() );
		}

		this->spaceMap->mapFunctionality.missilesPaternMap[ (MissileType) i ] = modelIt->second;
	}
	

	//---------------------------------------------


	this->starMap = new RadarMapGame( *this->render );


	this->initialized = true;
}


MapViewerApp::MapViewerApp( const string appName, Context &context, OpenGlSharedData &sharedData ): AbstractAPP( appName, context ), sharedData( sharedData)
{
	this->useMicroMap = true;

	this->render = NULL;
	this->spaceMap = NULL;
	this->starMap = NULL;
	this->animationManager = NULL;
	this->activeShipId = false;

	//set some default window res
	this->windowRes[0] = 640;
	this->windowRes[1] = 480;

	
	this->initialized = false;
}

MapViewerApp::~MapViewerApp(void)
{
	if (spaceMap != NULL) {
		delete this->spaceMap;
	}

	if ( starMap != NULL) {
		delete this->starMap;
	}

	if (render != NULL) {
		delete this->render;
	}

	if (animationManager != NULL) {
		delete this->animationManager;
	}

}


void MapViewerApp::removeSpaceMapObjectsOlderThan( double simulationDtTime, bool removeShips, bool removePlanets )
{
	if ( this->spaceMap == NULL )
	{
		return;
	}

	this->spaceMap->removeNotUpdatedObjects( simulationDtTime, removeShips, removePlanets );
}

void MapViewerApp::removeRadarObjectsOlderThan( double simulationDtTime, bool removeShips, bool removeStars )
{
	if ( this->starMap == NULL )
	{
		return;
	}

	//###########################################
	//remove old stars from Radar
	if ( removeStars )
	{
		map< unsigned long, StarRadarObject >::iterator starIt = this->starMap->RadarData.stars.begin(); 
		while ( starIt != this->starMap->RadarData.stars.end() )
		{
			if ( starIt->second.lastSynchronizationTime > simulationDtTime )
			{
				starIt = this->starMap->RadarData.stars.erase( starIt );
			}
			else
			{
				++starIt;
			}
		}
	}
	//###########################################
	//remove old ships from Radar
	if ( removeShips )
	{
		map< unsigned long, ShipRadarObject >::iterator shipIt = this->starMap->RadarData.ships.begin(); 
		while ( shipIt != this->starMap->RadarData.ships.end() )
		{
			if ( shipIt->second.lastSynchronizationTime > simulationDtTime )
			{
				shipIt = this->starMap->RadarData.ships.erase( shipIt );
			}
			else
			{
				++shipIt;
			}
		}
	}
}

void MapViewerApp::centerCameraToShip( unsigned long shipId )
{
	if ( this->spaceMap != NULL )
	{
		this->spaceMap->centerCameraToShip( shipId );
	}

	if ( this->starMap != NULL ) 
	{
		this->starMap->centerCameraToShip( shipId );
	}
}
void MapViewerApp::activateWarpEffect(  const Point_3<double> &microPos, const Point_3<double> &macroPos   )
{
	if ( this->spaceMap != NULL )
	{
		this->spaceMap->mapFunctionality.newWarpEffect( microPos );
	}
}
void MapViewerApp::setActiveShipId( unsigned long shipId )
{
	this->activeShipId = shipId;
}

void MapViewerApp::sendRadarWave( void )
{
	if ( !this->initialized ) return;
	if ( this->spaceMap == NULL ) return;
	if ( this->starMap == NULL ) return;

	if ( this->spaceMap != NULL )
	{
		this->spaceMap->activateRadar( this->activeShipId );
	}

	if ( this->starMap != NULL ) 
	{
		this->starMap->activateRadar( this->activeShipId );
	}

}
void MapViewerApp::signalizeShip( unsigned long shipId )
{
	if ( !this->initialized ) return;

	if ( this->spaceMap != NULL )
	{
		//this->spaceMap->setActiveShipId( this->activeShipId );
		this->spaceMap->signalizeShip( shipId );
	}

	if ( this->starMap != NULL ) 
	{
		//this->starMap->setActiveShipId( this->activeShipId );
		this->starMap->signalizeShip( shipId );
	}

}


void MapViewerApp::simulate( float dt )
{
	if ( !this->initialized ) return;

	if ( this->useMicroMap && this->spaceMap !=NULL )
	{
		this->spaceMap->handleKeyboard( this->keyboard );
		this->spaceMap->handleMouse( this->mouse );
	}
	else if  ( !this->useMicroMap && this->starMap !=NULL )
	{
		this->starMap->handleKeyboard( this->keyboard );
		this->starMap->handleMouse( this->mouse );
	}

	this->spaceMap->simulate( dt );
	this->starMap->simulate( dt );
	
	//the delete mouse events we dont want to process again in next loop
	this->mouse.pressedButtons[ Mouse::LEFT ] = false;
	this->mouse.clearDiff();
	this->mouse.wheel = 0;

}
void MapViewerApp::run(void)
{
	if ( !this->initialized )
	{
		KODIAK_DEBUG_COUT<<"MapViewerApp cannot render, is not initialized -> ignored"<<KODIAK_ENDL;
		return;
	}

	if ( this->sharedData.shaderManager != NULL )
	{
		this->sharedData.shaderManager->unbind();
	}
	
	
	if ( this->useMicroMap && this->spaceMap !=NULL )
	{
		this->spaceMap->setActiveShipId( this->activeShipId );
		this->spaceMap->render();
	}
	else if  ( !this->useMicroMap && this->starMap !=NULL  )
	{
		this->starMap->setActiveShipId( this->activeShipId );
		this->starMap->render();
	}
}

void MapViewerApp::setOnShipNewDestinationPosCallbackFce( void (*callbackFceMacro)( Point_3<double> ), void (*callbackFceMicro)( Point_3<double> )  )
{
	if ( this->starMap == NULL || this->spaceMap == NULL )
	{
		KODIAK_DEBUG_COUT<<"MapViewerApp: cannot set OnShipNewDestinationPosCallbackFce, this->starMap == NULL || this->spaceMap == NULL  ->ignored";
		return;
	}

	this->starMap->onShipNewDestinationPos = callbackFceMacro;
	this->spaceMap->mapFunctionality.onShipNewDestinationPos = callbackFceMicro;
}
GalaxyPart* MapViewerApp::getGalaxyPart(void) 
{ 
	if ( this->initialized && this->spaceMap )
	{
		return &(this->spaceMap->getGalaxyPart());
	}

	return NULL;
}
bool MapViewerApp::isInitialized(void) 
{ 
	return this->initialized; 
}
bool MapViewerApp::isMicroMapMode( void) 
{ 
	return this->useMicroMap; 
}
void MapViewerApp::setMicroMapMode( bool useMicroMap)
{ 
	this->useMicroMap = useMicroMap; 
}