#include "SpaceStructures.h"
#include <algorithm> 
#include "..\gameSettings.h"


MissileTarget::MissileTarget(void):enemyTargetId(false), targetType(NO_TARGET) 
{
}

void BaseSpaceObject::render( SoftProjRender& render,float levelOfDetail )
{
	this->internalRender( render, levelOfDetail );
}

BasePlanetClass::BasePlanetClass(void)
{
	this->enemyTargetIdContainer.clear();		/**< Container of all enemy targets.  */
	this->rateOfFire = 50.0;									/**< Cadency of the planet. */
	this->fontTexId = 0;
	this->name.clear();
	this->center.set_zero();
	this->axis.set_data( 0,1,0);
	this->speed = 0;
	this->angle = 0;
}
void BasePlanetClass::internalSimulate( double dt )
{
	this->angle += convertNanoAngleToAngle(speed) * dt;

	while ( this->angle > 360.0 )
	{
		this->angle -= 360.0;
	}

	while ( this->angle < 0 )
	{
		this->angle += 360.0;
	}
}
Point_3<double> BasePlanetClass::getPos(void)
{
	Vector_3<double> offset(1,0,0);
	this->axis.normalize();
	offset.rotate_around_vector( this->axis, -this->angle );
	const Point_3<double> planetPos( this->center + offset * this->radius );

	return planetPos;
}

void BasePlanetClass::setPos( const Point_3<double>& pos )
{
	this->center = pos;
}

void BasePlanetClass::render( SoftProjRender& render, float levelOfDetail  )
{
	this->internalRender( render, levelOfDetail  );
}

double BasicPlanet::getSize(void)
{
	return this->objectSize;
}

BasicPlanet::BasicPlanet(void)
{
	this->objectSize = 0;
	this->color.col = 1.0;
	this->color.w = 1.0;
	this->coronaTextureId = 0;
	this->textrureId = 0;
}

void BasicPlanet::internalRender( SoftProjRender& render, float levelOfDetail )
{
	Billboard corona;
	corona.color.col.Set( 1,1,1);
	corona.color.w = 1.0;
	corona.position =  CONVERT_TO_FLOAT(this->getPos());
	corona.size[0] = this->objectSize *1.3;
	corona.size[1] = this->objectSize * 1.3;
	corona.texID = this->coronaTextureId;

	glDepthMask(GL_FALSE);
	glEnable( GL_BLEND );
	glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
		render.renderBillboard( corona );
	glDisable( GL_BLEND );
	glDepthMask(GL_TRUE);


	//max & min circle segments
	const int MAX_CIRCLE_SEGMENT_COUNT = 800;
	const int MIN_CIRCLE_SEGMENT_COUNT = 40;

	const int finalCircleSegmentCount = max( MIN_CIRCLE_SEGMENT_COUNT, MAX_CIRCLE_SEGMENT_COUNT / levelOfDetail );

	//max & min sphere segments
	const int MAX_SPHERE_SEGMENT_AND_STACK_COUNT = 80;
	const int MIN_SPHERE_SEGMENT_AND_STACK_COUNT = 4;

	const int finalSphereSegmentCount = max( MIN_SPHERE_SEGMENT_AND_STACK_COUNT, MAX_SPHERE_SEGMENT_AND_STACK_COUNT / levelOfDetail );

	render.renderCircle( CONVERT_TO_FLOAT(this->center),  CONVERT_TO_FLOAT(this->axis), CRgbw( 0.6, 1.0 ), this->radius, finalCircleSegmentCount, 2.5 );
	render.renderSphere( CONVERT_TO_FLOAT(this->getPos()), this->color, this->objectSize , finalSphereSegmentCount, this->textrureId);


}

#if MODEL_PLANET_IMPLEMENTED
ModelPlanet::ModelPlanet( const Model &model): model( model )
{
	this->model.identity();
}

double ModelPlanet::getSize(void)
{
	//scale according to the axisY
	return this->model.getModelMatrixRef()[5];
}
void ModelPlanet::internalRender( SoftProjRender& render, int levelOfDetail  )
{
	render.renderCircle( CONVERT_TO_FLOAT(this->center),  CONVERT_TO_FLOAT(this->axis), CTriCol( 0.6 ), this->radius, 30, 1.0 );
	
	Vector_3<double> offset = this->axis.getPerpendicularVector();
	
	if ( offset != Vector_3<double>(0,0,0) )
	{	
		offset.normalize();
	}

	offset.rotate_around_vector( this->axis, this->angle );

	Point_3<double> pos=  this->center + offset * this->radius;

	this->model.getModelMatrixRef()[12] = pos.x();
	this->model.getModelMatrixRef()[13] = pos.y();
	this->model.getModelMatrixRef()[14] = pos.z();

	render.renderModel( this->model );
}
#endif

void GalaxyPart::releaseResources(void)
{
	for ( int i=0; i< this->spacePlanets.size(); ++i)
	{
		delete this->spacePlanets[i];
	}

	for ( int i=0; i< this->spaceShips.size(); ++i)
	{
		delete this->spaceShips[i];
	}

	
	this->spacePlanets.clear();
	this->spaceShips.clear();
}

BasePlanetClass * GalaxyPart::findPlanet( const unsigned long planetId  )
{
	return GalaxyPart::findPlanet( this->spacePlanets, planetId );
}

SpaceShip * GalaxyPart::findShip( const unsigned long shipId )
{
	return GalaxyPart::findShip( this->spaceShips, shipId );
}

BasePlanetClass *GalaxyPart::findPlanet(  const PlanetDatabase& spacePlanet, const unsigned long planetId  )
{
	PlanetDatabase::const_iterator planetIt = spacePlanet.find( planetId );

	if ( planetIt !=  spacePlanet.end() )
	{
		return planetIt->second;
	}
	else
	{
		return NULL;
	}
}
SpaceShip *GalaxyPart::findShip(  const ShipDatabase& spaceShip, const unsigned long shipId  )
{
	ShipDatabase::const_iterator shipIt = spaceShip.find( shipId );

	if ( shipIt !=  spaceShip.end() )
	{
		return shipIt->second;
	}
	else
	{
		return NULL;
	}
}

bool GalaxyPart::removePlanet( PlanetDatabase& spacePlanet, const unsigned long planetId, PlanetDatabase::iterator &outPlanetIterator )
{
	PlanetDatabase::iterator planetIt = spacePlanet.find( planetId );

	if ( planetIt ==  spacePlanet.end() )
	{
		//could not be deleted, not found!
		return false;
	}
	else
	{
		if ( planetIt->second != NULL )
		{
			delete planetIt->second;
			planetIt->second = NULL;
		}

		outPlanetIterator = spacePlanet.erase( planetIt );

		//successfully deleted
		return true;
	}
}

bool GalaxyPart::removeShip( ShipDatabase& spaceShip, const unsigned long shipId, ShipDatabase::iterator &outShipIterator )
{
	ShipDatabase::iterator planetIt = spaceShip.find( shipId );

	if ( planetIt ==  spaceShip.end() )
	{
		//could not be deleted, not found!
		return false;
	}
	else
	{
		if ( planetIt->second != NULL )
		{
			delete planetIt->second;
			planetIt->second = NULL;
		}

		outShipIterator = spaceShip.erase( planetIt );

		//successfully deleted
		return true;
	}
}

bool GalaxyPart::removeShip( const unsigned long shipId, ShipDatabase::iterator &outShipIterator )
{
	return GalaxyPart::removeShip( this->spaceShips, shipId, outShipIterator );
}

bool GalaxyPart::removePlanet( const unsigned long planetId, PlanetDatabase::iterator &outPlanetIterator )
{
	return GalaxyPart::removePlanet( this->spacePlanets, planetId, outPlanetIterator );
}

SpaceShip *GalaxyPart::insertShip( const unsigned long shipId, const SpaceShip& newShip)
{
	//delete old record if exist
	BaseSpaceObject *objPtr = NULL;
	objPtr = this->findShip( shipId );
	if ( objPtr !=NULL )
	{
		delete objPtr;
		objPtr = NULL;
	}

	//insert new record
	SpaceShip *newShipRecord =  new SpaceShip( newShip );
	this->spaceShips[ shipId ] = newShipRecord;

	return newShipRecord;
}

BasicPlanet *GalaxyPart::insertPlanet(  const unsigned long planetId, const BasicPlanet& newPlanet )
{
	//delete old record if exist
	BaseSpaceObject *objPtr = NULL;
	objPtr = this->findPlanet( planetId );
	if ( objPtr !=NULL )
	{
		delete objPtr;
		objPtr = NULL;
	}

	//insert new record
	BasicPlanet *newPlanetRecord = new BasicPlanet( newPlanet );
	this->spacePlanets[ planetId ] = newPlanetRecord;

	return newPlanetRecord;
}

SpaceShip::SpaceShip( const Model &model, const Vector_3<double> frontVector ): model(model)
{
	this->setFront( frontVector );
	this->accleration = 0;
	this->speed = 0;
	this->shieldPercentage = 0;
	this->lifePercentage = 0;
	this->externForce.set_zero();
	this->name.clear();
	this->type = (ShipType)0;
	this->orientationVector.set_zero();

	this->isOrbiting = false;
	this->isMiningFromPlanet = false;
	this->orbitPivot.set_zero();
	this->orbitSpeed = 0;

	this->enemyTarget.enemyTargetId = false;
	this->enemyTarget.targetType =	NO_TARGET;

	this->inWarp = false;
	this->inBase = false;
}
void SpaceShip::internalRender( SoftProjRender& render,float levelOfDetail  )
{
	render.renderModel( this->model );
}
void SpaceShip::setExternForce( const Vector_3<double> &externForceVector )
{
	this->externForce = externForceVector;
}
Vector_3<double> SpaceShip::getExternForce(void) const
{
	return this->externForce;
}
Point_3<double> SpaceShip::getPos(void)
{
	Point_3<double> shipPos;
	shipPos.x()= this->model.getModelMatrixRef()[12];
	shipPos.y()= this->model.getModelMatrixRef()[13];
	shipPos.z()= this->model.getModelMatrixRef()[14];

	return shipPos;
}
void SpaceShip::setPos( const Point_3<double>& pos )
{
	this->model.getModelMatrixRef()[12] = pos.x();
	this->model.getModelMatrixRef()[13] = pos.y();
	this->model.getModelMatrixRef()[14] = pos.z();

}
Vector_3<double> SpaceShip::getDir(void)
{
	Vector_3<double> dir;

	if ( this->isOrbiting )
	{
		//direciton is tangent vector if we are orbiting
		Vector_3<double> tangentVector( this->orbitPivot - this->getPos() );
		tangentVector.rotate_around_vector( Vector_3<double>(0,1,0), 90.0 );
		dir = tangentVector;
	}
	else
	{
		dir = this->destinationPoint - this->getPos();
	}

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

	return dir;
}
double SpaceShip::getSize(void)
{
	//size is calculated from axisY 
	return this->model.getModelMatrixRef()[5];
}
void SpaceShip::setFront( const Vector_3<double> &front )
{
	this->orientationVector = front;
}
Vector_3<double> SpaceShip::getOrientation(void) const
{
	return this->orientationVector;
}
void SpaceShip::setOrientation( Vector_3<double> &orientation )
{
	if ( orientation == Vector_3<double>(0,0,0) )
	{
		//do nothing, keep last valid data
		return;
	}

	//NOTE: the ship is moving only in xz plane
	assert( abs(orientation.y() ) < INTERVAL_TOLERANCE && "Only XZ vector components" );
	
	double angle = this->orientationVector.get_angle_Ang_xz( orientation );

	Vector_3<double> perpVector( -this->orientationVector.z(), 0, this->orientationVector.x() );
	double hlpAngle = perpVector.get_angle_Ang_xz( orientation );

	if ( abs(hlpAngle) > 90.0 - INTERVAL_TOLERANCE )
	{
		//revert orientation
		angle = -angle;
	}

	this->orientationVector = orientation;
	this->model.rotate( 0, angle, 0 );
}

void SpaceShip::internalSimulate( double dt )
{
	
	if ( this->isOrbiting )
	{
		//orbiting simulation
		Point_3<double> position = this->getPos();
		position.rotate_point( this->orbitPivot, Vector_3<double>(0,1,0), - this->orbitSpeed * dt );

		this->model.positionX() = position.x();
		this->model.positionY() = position.y();
		this->model.positionZ() = position.z();

		this->setOrientation( this->getDir() );
	}
	else
	{
		//non orbiting simulation
		float mediumSpeed = this->speed + this->accleration * dt / 2;

		const float HALF_LIGHT_SPEED= 149896.229 / GameConstants::MEASURING_MICRO_SCALE;
		
		float realSpeed;
		
		if ( this->inWarp )
		{
			realSpeed	= mediumSpeed;
		}
		else
		{
			realSpeed = min(mediumSpeed, HALF_LIGHT_SPEED);
		}

		Vector_3<double> offset= this->getDir() * realSpeed * dt;
		double offsetSize = offset.get_length();
		double remainingDistance = (this->getPos() - this->destinationPoint).get_length();

		float finalOffsetSize = min( remainingDistance, offsetSize);
		Vector_3<double> finalOffsetVector(  this->getDir() * finalOffsetSize );


		this->model.positionX() += finalOffsetVector.x();
		this->model.positionY() += finalOffsetVector.y();
		this->model.positionZ() += finalOffsetVector.z();


		this->speed += this->accleration * dt;

		if ( ! this->inWarp )
		{
			this->speed = min( this->speed, HALF_LIGHT_SPEED);
		}

	}

//	Vector_3<double> offset= (this->getDir() * this->speed + this->externForce) * dt;

	
}


// #####################################################3

SpaceMissile::SpaceMissile( const Model &model, const Point_3<double> &dstPos, const Vector_3<double> frontVector  ) : model(model)
{
	//this->targetObject = targetObject;
	this->dstPos = dstPos;
	this->orientationVector = frontVector;
}

void SpaceMissile::internalRender( SoftProjRender& render, float levelOfDetail  )
{
	render.renderModel( this->model );
}

Point_3<double> SpaceMissile::getPos(void)
{
	Point_3<double> pos;
	pos.x()= this->model.getModelMatrixRef()[12];
	pos.y()= this->model.getModelMatrixRef()[13];
	pos.z()= this->model.getModelMatrixRef()[14];

	return pos;
}
double SpaceMissile::getSize(void)
{
	//size is calculated from axisY 
	return this->model.getModelMatrixRef()[5];
}
void SpaceMissile::setPos( const Point_3<double>& pos )
{
	this->model.getModelMatrixRef()[12] = pos.x();
	this->model.getModelMatrixRef()[13] = pos.y();
	this->model.getModelMatrixRef()[14] = pos.z();

}
Vector_3<double> SpaceMissile::getDir(void)
{
	Vector_3<double> dir = this->dstPos- this->getPos();

	if ( dir != Vector_3<double>(0,0,0) )
	{
		dir.normalize();
	}
	return dir;
}
void SpaceMissile::setFront( const Vector_3<double> &front )
{
	this->orientationVector = front;
}
void SpaceMissile::setOrientation( Vector_3<double> &orientation )
{
	//NOTE: the missile is moving only in xz plane
	assert( abs(orientation.y() ) < INTERVAL_TOLERANCE && "Only XZ vector components" );
	
	double angle = this->orientationVector.get_angle_Ang_xz( orientation );

	Vector_3<double> perpVector( -this->orientationVector.z(), 0, this->orientationVector.x() );
	double hlpAngle = perpVector.get_angle_Ang_xz( orientation );

	if ( abs(hlpAngle) > 90.0 + INTERVAL_TOLERANCE )
	{
		//revert orientation
		angle = -angle;
	}
	

	this->orientationVector = orientation;
	this->model.rotate( 0, angle, 0 );
}

void SpaceMissile::internalSimulate( double dt )
{
	Vector_3<double> offset= this->getDir() * this->speed * dt;

	this->model.positionX() +=offset.x();
	this->model.positionY() +=offset.y();
	this->model.positionZ() +=offset.z();

	this->setOrientation( this->getDir() );
}

// ##################################################
//RADAR

Signalizer::Signalizer( const Point_3<double> &position, const double maxRadius, const double radiusOffsetDt )
		: position( position), MAX_RADIUS( maxRadius ), currentRadius( 0.0),  RADIUS_OFFSET_DT(radiusOffsetDt)
{
}

void Signalizer::simulate( const double dt ) 
{ 
	if ( this->isActive() )
	{
		this->currentRadius+= dt * RADIUS_OFFSET_DT;  
	}
}

bool Signalizer::isActive(void)
{
	return ( this->currentRadius< MAX_RADIUS );
}

void Signalizer::render( SoftProjRender &render )
{
	if ( this->isActive() )
	{
		CRgbw color;
		color.col = GameConstants::SCIFI_GREEN_BRIGHT.col;
		color.w = (MAX_RADIUS - this->currentRadius) / MAX_RADIUS;

		const Vector_3<double> axisY( 0, 1, 0);
		render.renderCircle(	CONVERT_TO_FLOAT(this->position)			/*Position*/
								,CONVERT_TO_FLOAT(axisY)					/*Circle axis*/
								,color										/*Circle color*/
								,this->currentRadius						/*circle radius*/
								,GameConstants::CIRCLE_SEGMENT_COUNT		/*circle segment count*/
								);
	}
}
