#include "RadarMapGameInterface.h"
#include "streams\ErrorStream.h"
extern ExceptionStream< ostream&, ContinueFunctor  >		KODIAK_WARNING;
extern KODIAK_DEBUG_STREAM KODIAK_DEBUG_COUT;

void RadarObject::setPosition( const Point_3<double>& pos )
{
	this->position = pos;
}

Point_3<double> RadarObject::getPosition(void) const
{
	return this->position;
}

RadarObjectType RadarObject::getObjectType( void ) const
{
	return this->objectType;
}
	
void RadarObject::simulate( double dt )
{
	this->lastSynchronizationTime+= dt; ///total (summed) dt since last synchronization;
	this->internalSimulate( dt );
}

RadarObject::RadarObject( const RadarObjectType objectType, const Point_3<double> &position )
	: objectType( objectType ), position( position ), lastSynchronizationTime( 0 )
{
}

ShipRadarObject::ShipRadarObject( void )
	: RadarObject( Ship, MAKE_POINT<double>(0,0,0)  ), dstPosition( 0,0,0 ), speed( 0 ), 
	  acceleration(0), alpha( 1.0 ), alphaOffsetDt( 0.001 ), inWarp(false), inBase(false)
{
}



void ShipRadarObject::setSpeed( double speed )
{
	this->speed = speed;
}

void ShipRadarObject::setAcceleration( double acceleration )
{
	this->acceleration = acceleration;
}
void ShipRadarObject::setAlpha( float alpha )
{
	this->alpha = alpha;
}
void ShipRadarObject::setAlphaOffset( float alphaOffsetDt )
{
	this->alphaOffsetDt = alphaOffsetDt;
}
void ShipRadarObject::internalSimulate( double dt )
{
	Vector_3<double> moveVector( this->dstPosition - this->position );

	if ( moveVector != MAKE_VECTOR<double>(0,0,0) )
	{
		moveVector.normalize();
	}

	this->speed += this->acceleration * dt; 
	this->position += moveVector * ( this->speed * dt );
	this->alpha -= dt * this->alphaOffsetDt;
}

void ShipRadarObject::render( SoftProjRender &renderer, const Billboard &srcBillboard )
{
	Billboard billboardCopy( srcBillboard );
	billboardCopy.position = CONVERT_TO_FLOAT(this->position);

	glEnable( GL_BLEND );
	glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	renderer.renderBillboard( billboardCopy );
}

void ShipRadarObject::setDstPosition( const Point_3<double>& dstPos )
{
	this->dstPosition = dstPos;
}
Point_3<double> ShipRadarObject::getDstPosition(void) const
{
	return this->dstPosition;
}

bool ShipRadarObject::isVisible( void ) const
{
	return this->alpha > 0;
}

StarRadarObject::StarRadarObject( const Point_3<double> &position ) 
	:RadarObject( Star, position )
{
}

StarRadarObject::StarRadarObject( void ) 
	:RadarObject( Star, MAKE_POINT<double>(0,0,0) )
{
}

void StarRadarObject::render( SoftProjRender &renderer, const Billboard &srcBillboard )
{
	glEnable( GL_BLEND );
	glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

	Billboard billboardCopy( srcBillboard );
	billboardCopy.position = CONVERT_TO_FLOAT(this->position);
	renderer.renderBillboard( billboardCopy );
}

RadarMapGame::RadarMapGame( SoftProjRender &renderer   ): renderer( renderer ), NEAR_CAMERA_ZOOM( 50 ), FAR_CAMERA_ZOOM( 800 ), MAP_BOUNDARY( 1000 ), signalizer( Signalizer( MAKE_POINT<double>(0,0,0), 0, 0))
{
	this->foregroundTex = TextureManager::getInstance().getTexture( "tvTexture" );
	this->shipTex = TextureManager::getInstance().getTexture( "shipRadar" );
	this->starTex = TextureManager::getInstance().getTexture( "starRadar" );
	this->backgroundTex =  TextureManager::getInstance().getTexture( "backgroundRadar" );


	this->cameraPivot = MAKE_POINT<double>( 0,0,0);
	this->camera.pos = this->cameraPivot - Vector_3<double>(0,1,0) * NEAR_CAMERA_ZOOM;
	this->camera.look.set_data( 0, 1, 0);
	this->camera.right = MAKE_VECTOR<double>(1,0,0);

	this->refreshCamera();


	this->newDstPoint = false;
	this->dstPoint[0] = 0;
	this->dstPoint[1] = 0;

	this->activeShipId = false;
	this->onShipNewDestinationPos = NULL;
}

void RadarMapGame::simulate( float dt )
{
	// ships
	for ( map< unsigned long, ShipRadarObject >::iterator it=this->RadarData.ships.begin(); it != this->RadarData.ships.end(); ++it )
	{
		//display ship if its not in base
		if (  ! it->second.inBase /*&& !it->second.inWarp*/ )
		{
			it->second.simulate( dt );
		}

		if ( ! it->second.isVisible() )
		{
			//remove invisible ships except active Ship, which cannot be removed
			if (  this->getActiveShipPtr() != &it->second )
			{
				it = this->RadarData.ships.erase( it );
			}
			
		}
	}

	// planets
	for ( map< unsigned long, StarRadarObject >::iterator it=this->RadarData.stars.begin(); it != this->RadarData.stars.end(); ++it )
	{
		it->second.simulate( dt );
	}

	this->signalizer.simulate( dt );
}
void RadarMapGame::activateRadar( unsigned long shipId )
{
	ShipRadarObject *shipPtr = this->getShipPtr( shipId );

	if ( shipPtr == NULL ) return;

	const float MAX_RADIUS = 20;
	const float RADIUS_OFFSET = 0.5;
	this->signalizer = Signalizer( shipPtr->getPosition(), MAX_RADIUS, RADIUS_OFFSET);
}
void RadarMapGame::signalizeShip( unsigned long shipId )
{
	ShipRadarObject *shipPtr = this->getShipPtr( shipId );

	if ( shipPtr == NULL ) return;

	const float MAX_RADIUS = 20;
	const float RADIUS_OFFSET = 0.5;
	this->signalizer = Signalizer( shipPtr->getPosition(), MAX_RADIUS, RADIUS_OFFSET);
}
void RadarMapGame::render( void )
{
	this->renderer.getProjection().zNear = 10.0;
	this->renderer.getProjection().zFar = 1000.0;
	this->refreshCamera();
	

	this->renderer.beginFrame( this->camera );
	glDisable( GL_BLEND );
	glEnable( GL_DEPTH_TEST );

	//this->renderer.renderFullscreenImage2D( 2 );

	this->renderInvisiblePickPlane( this->renderer, CONVERT_TO_FLOAT(this->cameraPivot) );

	// recalculate new destination point if it changed
	// must be here because we need Render object do be able to do unproject
	if ( this->newDstPoint && this->getActiveShipPtr() != NULL &&
		 !this->getActiveShipPtr()->inBase /*&& !this->getActiveShipPtr()->inWarp*/ )
	{
		Point_3<double> clickPos3D;
		int int2[2] = { this->dstPoint[0] ,  this->renderer.viewport.sizeY - this->dstPoint[1] };

		clickPos3D =  this->renderer.unproject( int2 );
		clickPos3D.y() = 0; //ignore y ( only X,Z are relevant )
	
		this->getActiveShipPtr()->setDstPosition( CONVERT_TO_DOUBLE(clickPos3D) );

		if ( this->onShipNewDestinationPos != NULL )
		{
			this->onShipNewDestinationPos( clickPos3D );
		}

		this->newDstPoint = false;
	}

	//active ship is always visible!!!
	if ( this->getActiveShipPtr() != NULL  )
	{
		this->getActiveShipPtr()->setAlpha( 1.0 );
	}

	glDisable( GL_DEPTH_TEST );

	glDisable( GL_BLEND );
	glColor3f( 0.5, 0.3, 0.3 );
	this->renderer.renderFullscreenImage2D( this->backgroundTex );

	this->signalizer.render( this->renderer );
		
	//-----------------------------------------------------------
	//render stars
	const CRgbw blue( 0.1, 0.1, 1.0,1.0 ); 
	Billboard starBillboard;
	starBillboard.color = blue; 
	starBillboard.size[0] = 2.0;
	starBillboard.size[1] = 2.0;
	starBillboard.texID = this->starTex;

	for ( map< unsigned long, StarRadarObject >::iterator it=this->RadarData.stars.begin(); it != this->RadarData.stars.end(); ++it )
	{
		it->second.render(  this->renderer, starBillboard );
	}

	//------------------------------------------------------------
	//render ships
	const CRgbw green( 0.1, 1.0, 0.1,1.0 ); 
	Billboard shipBillboard;
	shipBillboard.color = green; 
	shipBillboard.size[0] = 0.6;
	shipBillboard.size[1] = 0.6;
	shipBillboard.texID = this->shipTex;


	for ( map< unsigned long, ShipRadarObject >::iterator it=this->RadarData.ships.begin(); it != this->RadarData.ships.end(); ++it )
	{
		//display ship if its not in base or in warp
		if (  ! it->second.inBase && !it->second.inWarp )
		{
			it->second.render(  this->renderer, shipBillboard );
		}
	}
	
	
	// render active ship with red color + render path line -----------------------
	if ( this->getActiveShipPtr() != NULL 
		&& !this->getActiveShipPtr()->inBase /*&& !this->getActiveShipPtr()->inWarp*/ )
	{
		glDisable( GL_BLEND );
		const CRgbw red( 1.0, 0.1, 0.1, 1.0 ); 
		shipBillboard.color = red;
		this->getActiveShipPtr()->render( this->renderer, shipBillboard );
		renderer.renderLine( CONVERT_TO_FLOAT(this->getActiveShipPtr()->getPosition()), CONVERT_TO_FLOAT(this->getActiveShipPtr()->getDstPosition()), red );
	}
	// ----------------------------------------------------------------------------




	// render screen texture over whole viewport
	glEnable( GL_BLEND );
	glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	glColor3f( 1,1,1 );
	this->renderer.renderFullscreenImage2D( this->foregroundTex );

	this->renderer.endFrame();
}

void RadarMapGame::refreshCamera(void)
{
	float len = (this->cameraPivot - this->camera.pos).get_length();
	this->camera.pos = this->cameraPivot - this->camera.look * len;
	this->camera.look.set_data( 0, 1, 0);
	
	this->camera.right = Vector_3<double>(1,0,0);
	this->camera.recalculate_up();

	//Warning: this clamp values near zero to zero, do not delete!!!
	if ( this->cameraPivot == Point_3<double>(0,0,0) )
	{
		this->cameraPivot =  Point_3<double>(0,0,0);
	};
}

void RadarMapGame::zoomCamera( float zoom )
{
	float diffY = min( NEAR_CAMERA_ZOOM-1, zoom );
	this->camera.move_camera( diffY );
	float distance = this->camera.pos.get_distance( this->cameraPivot );

	if ( distance < NEAR_CAMERA_ZOOM )
	{
		this->camera.look =  this->cameraPivot - this->camera.pos;
		this->camera.look.normalize();
		this->camera.pos = this->cameraPivot - this->camera.look * NEAR_CAMERA_ZOOM; //move camera to NEAR_LIMIT distance
	}
	else if ( distance > FAR_CAMERA_ZOOM )
	{
		this->camera.look = this->cameraPivot - this->camera.pos;
		this->camera.look .normalize();
		this->camera.pos = this->cameraPivot - this->camera.look * FAR_CAMERA_ZOOM; //move camera to NEAR_LIMIT distance
	}

	
}

void RadarMapGame::handleKeyboard( Keyboard &keyboard )
{
	Vector_3<double> moveVector;

	//move camera pivot if LEFT, RIGHT, UP, DOWN is pressed
	if ( keyboard.isPressed( Qt::Key_Left ) )
	{
		moveVector.x() = -1.0;
	}
	if ( keyboard.isPressed( Qt::Key_Right ) )
	{
		moveVector.x() = 1.0;
	}
	if ( keyboard.isPressed( Qt::Key_Up ) )
	{
		moveVector.z() = 1.0;
	}
	if ( keyboard.isPressed( Qt::Key_Down ) )
	{
		moveVector.z() = -1.0;
	}

	this->cameraPivot += moveVector;
	this->refreshCamera();

	if ( keyboard.isPressed( Qt::Key_Plus ) )
	{
		this->zoomCamera( 1.5 );
	}

	if ( keyboard.isPressed( Qt::Key_Minus ) )
	{
		this->zoomCamera( -1.5 );
	}

	
}

void RadarMapGame::handleMouse( Mouse &mouse )
{
	if ( mouse.pressedButtons[  Mouse::RIGHT ] )
	{
		Vector_3<double> moveVector;

		moveVector.x() = mouse.getDiffX() / 5.0;
		moveVector.z() = -mouse.getDiffY() / 5.0;

		this->cameraPivot += moveVector;
	}
	else if ( mouse.pressedButtons[  Mouse::LEFT ] )
	{
		this->newDstPoint = true;
		this->dstPoint[0] = mouse.getPosX();
		this->dstPoint[1] = mouse.getPosY();
	}

	this->zoomCamera(  mouse.wheel * 0.1 ); 
	this->refreshCamera();
}

ShipRadarObject *RadarMapGame::getActiveShipPtr(void)
{
	return this->getShipPtr( this->activeShipId );
}
ShipRadarObject *RadarMapGame::getShipPtr( unsigned long shipId)
{
	map< unsigned long, ShipRadarObject >::iterator it = this->RadarData.ships.find( shipId );

	if ( it == this->RadarData.ships.end() )
	{
		//not found
		return NULL;
	}

	return &(it->second);
}
void RadarMapGame::setActiveShipId( unsigned long shipId )
{
	this->activeShipId = shipId;
}

void RadarMapGame::centerCameraToShip( unsigned long shipId )
{
	ShipRadarObject * shipPtr = this->getShipPtr( shipId );
	if ( shipPtr == NULL ) return;

	if ( shipPtr != NULL )
	{
		this->cameraPivot =  shipPtr->getPosition();
	}
	
	this->refreshCamera();
}

void RadarMapGame::renderInvisiblePickPlane( SoftProjRender &render, const Point_3<float> &center)
{
	glPushMatrix();
		Point_3<double> from( -MAP_BOUNDARY, 0, -MAP_BOUNDARY );
		Point_3<double> to( MAP_BOUNDARY, 0, MAP_BOUNDARY );
	
		from += CONVERT_TO_VECTOR<double>( CONVERT_TO_DOUBLE(center) );
		to += CONVERT_TO_VECTOR<double>( CONVERT_TO_DOUBLE(center) );

		render.bind();

		selectTextureUnit( 0 );
		glDisable( GL_TEXTURE_2D );

		glColor4f( 0,0,0,0); //invisible desk ( used for gluUnproject during picking )
		glBegin(GL_QUADS);
			glVertex3f( from.x(),  0, from.z() );
			glVertex3f( to.x(),  0, from.z() );
			glVertex3f( to.x(), 0 , to.z() );
			glVertex3f( from.x(), 0, to.z() );
		glEnd();
		
		
	glPopMatrix();
}