/******************************************************************************
*******************************************************************************

	Bubble Fight EX
	relminator
	http://rel.betterwebber.com


	Centipede class


*******************************************************************************
******************************************************************************/

#include "Centipede.h"

#define MIN_X ( -450 << 12 )
#define MIN_Y ( -18 << 12 )

#define MAX_CENTIPEDES 4


//------------------------------------------------------------------------------
//	Centipede Enemy
//------------------------------------------------------------------------------
Centipede::Centipede()
{
	Frame = 0;
	x = floattof32(300);
	y = floattof32(96);
	Active = false;
	Counter = 0;
	Hp = 128;
	DrawMode = EN_NORMAL;
	
	LastPoint.x = x;
	LastPoint.y = y;
		
	for( int i = 0; i < CENTIPEDE_MAX_SEGMENTS; i++ )
	{
		Segments[i].x = floattof32(300);
		Segments[i].y = floattof32(96);
	}

}

Centipede::~Centipede()
{
}


void Centipede::Update()
{
	
	Counter++;
	
	
	// Update spline 
	// and get out of loop when we get 
	// a greater linear distance than 6
	s32 DistFromLast = 0;
	while( DistFromLast < floattof32(6) )
	{
		// update spline
		Path.Update();
	
		x = Path.GetX();
		y = Path.GetY();
		
		// calculate distance of current point from last used point
		s32 dx = (( x - LastPoint.x ) * ( x - LastPoint.x )) >> 12;
		s32 dy = (( y - LastPoint.y ) * ( y - LastPoint.y )) >> 12;
		s32 d = sqrtf32( dx + dy );
		
		// increment linear distance
		DistFromLast += d;
	}	
	
	// save point for use next loop
	LastPoint.x = x;
	LastPoint.y = y;
	
	// set up our centipede
	Segments[CENTIPEDE_MAX_SEGMENTS - 1].x = x;
	Segments[CENTIPEDE_MAX_SEGMENTS - 1].y = y;

	for( int i = 0; i < (CENTIPEDE_MAX_SEGMENTS - 1);  i++ )
	{
		Segments[i].x = Segments[i+1].x;
		Segments[i].y = Segments[i+1].y;
	}

	
	
	if( x < MIN_X )
	{
		Active = false;
		x = 300 << 12;
		y = 96 << 12;
	}
	

}


void Centipede::LoadPath( int _sx, int _sy, int _ex, int _ey )
{
	Path.Init( 24 );
	
	Spline::Coord p;
	
	// start
	p.x = _sx;
	p.y = _sy;
	
	// we should use 2 points
	Path.AddPoint( p );
	Path.AddPoint( p );
	
	// randomize for now
	for (int i=0; i < 16; i++)
	{
		p.x = qran_range(0, 255);
		p.y = qran_range(0, 191);
		Path.AddPoint( p );
	}
	
		p.x = 129;
		p.y = 96;
		Path.AddPoint( p );
	
		p.x = 20;
		p.y = 96;
		Path.AddPoint( p );
	
	// close last 2 coords
	p.x = _ex;
	p.y = _ey;
	
	// we should use 2 points
	Path.AddPoint( p );
	Path.AddPoint( p );
	
	Path.SetSpeed(8);
	Path.SetT(0);
	
}
	

//--------------------------------------
//	Private Functions
//--------------------------------------


//------------------------------------------------------------------------------
//	Centipede Container
//------------------------------------------------------------------------------
//--------------------------------------
//	Ctor and Dtor
//--------------------------------------
CentipedeContainer::CentipedeContainer()
{
}
	
CentipedeContainer::~CentipedeContainer()
{
	Centipedes.clear();				// clear the bullets
}


void CentipedeContainer::Init( int Palette, glImage* const Sprites )
{
	ColorTable = Palette;		// init needed data for drawing
	Images = Sprites;
	Centipedes.resize( MAX_CENTIPEDES/2 );			// 1/2 of max (speed of iteration)
	Centipedes.reserve( MAX_CENTIPEDES );		// reserve max for speed.  No need to do this for enemies
}

//--------------------------------------
//	Virtual Functions
//--------------------------------------
void CentipedeContainer::UpdateEntities()
{
	
	std::vector<Centipede>::iterator iter;
	for( iter = Centipedes.begin(); iter != Centipedes.end(); ++iter )
	{
		if( iter->Active )
			iter->Update();
	}
	
}

void CentipedeContainer::DrawEntities()
{
	
	glColorTable( GL_RGB256, ColorTable );
	numEntities = 0;
	std::vector<Centipede>::iterator iter;
	for( iter = Centipedes.begin(); iter != Centipedes.end(); ++iter )
	{
		if( iter->Active )
		{
			numEntities++;
			if( (iter->DrawMode == EN_WHITE) )
			{
				iter->DrawMode = EN_NORMAL;
				glColorTable( GL_RGB256, Palettes::Instance()->Red() );
			
				for( int i = 0; i < CENTIPEDE_MAX_SEGMENTS; i+= 8 )
				{
					
					s32 tx = iter->Segments[i].x;
					s32 ty = iter->Segments[i].y;
					
					s32 angle;
					if( i == 0 )    //tail
					{
						angle = BRAD_PI + atan2Lerp( tx - iter->Segments[i+7].x, 
													 ty - iter->Segments[i+7].y 
												   );				
						iter->Frame = 0;					
					}	
					else if( i == (CENTIPEDE_MAX_SEGMENTS-8) )	// head
					{
						angle = BRAD_PI + atan2Lerp( iter->Segments[i-7].x - tx, 
													 iter->Segments[i-7].y - ty 
												   );
						iter->Frame = 0;
					}
					else	// segments
					{
						angle = BRAD_PI + atan2Lerp( iter->Segments[i-7].x - tx, 
													 iter->Segments[i-7].y - ty 
												   );
						iter->Frame = 1;
					}
					
					
					glSpriteRotate( tx >> 12,
									ty >> 12,
									angle,
									GL_FLIP_NONE, &Images[iter->Frame] 
								  );
							
				}  //next
				
				glColorTable( GL_RGB16, ColorTable );
					
			}   
			else	//draw normal
			{
				for( int i = 0; i < CENTIPEDE_MAX_SEGMENTS; i+= 8 )
				{
					
					s32 tx = iter->Segments[i].x;
					s32 ty = iter->Segments[i].y;
					
					s32 angle;
					if( i == 0 )    //tail
					{
						angle = BRAD_PI + atan2Lerp( tx - iter->Segments[i+7].x, 
													 ty - iter->Segments[i+7].y 
												   );				
						iter->Frame = 0;					
					}	
					else if( i == (CENTIPEDE_MAX_SEGMENTS-8) )	// head
					{
						angle = BRAD_PI + atan2Lerp( iter->Segments[i-7].x - tx, 
													 iter->Segments[i-7].y - ty 
												   );
						iter->Frame = 0;
					}
					else	// segments
					{
						angle = BRAD_PI + atan2Lerp( iter->Segments[i-7].x - tx, 
													 iter->Segments[i-7].y - ty 
												   );
						iter->Frame = 1;
					}
					
					
					glSpriteRotate( tx >> 12,
									ty >> 12,
									angle,
									GL_FLIP_NONE, &Images[iter->Frame] 
								  );
							
				}  // next
				
			} // endif color mode
		}
	}
	
}

bool CentipedeContainer::Collide( StarFighter &Star )
{
	std::vector<Centipede>::iterator iter;
	for( iter = Centipedes.begin(); iter != Centipedes.end(); ++iter )
	{
		if( iter->Active )
		{
			for( int i = 0; i < CENTIPEDE_MAX_SEGMENTS; i+= 8 )
			{
				int rad2 = 16 * 16;
				int cx = (iter->Segments[i].x >> 12) - (Star.x >> 12);
				int cy = (iter->Segments[i].y >> 12) - (Star.y >> 12);
				if( ( (cx * cx) + (cy * cy) ) < (rad2 * (1+Star.IsBursting())) )
				{
					if( Star.IsBursting() )
					{
						iter->Hp--;
						if( (iter->Hp & 3) == 0 ) Sound::Instance()->PlaySFX(E_SFX_HIT);
						if( iter->Hp < 0 )
						{
							iter->Active = false;
							Star.Score += 975;
							Sound::Instance()->PlaySFX(E_SFX_MEDEXP2);
							// Add some nice explosions
							for( int j = 0; j < CENTIPEDE_MAX_SEGMENTS; j+= 8 )
							{
								s32 dx = cosLerp(j * 128)*2;
								s32 dy = sinLerp(j * 256)*2;
								Explosion::Instance()->Spawn( iter->Segments[j].x >> 12,
														  iter->Segments[j].y >> 12,
														   dx,
														   dy,
														   EX_MEDIUM_4
														 );
							}
							
							iter->x = 600 << 12;
							iter->y = 96 << 12;
							
						}
						if( iter->DrawMode == EN_NORMAL )
						{
							if( (iter->Counter & 1) ) iter->DrawMode = EN_WHITE;
						}
					}
					else
					{
						// kill star
						if( !Star.IsInvincible() ) Star.Kill();
					}
					
					return true;
				}	
			}
			
		}
	}
	
	return false;
}


void CentipedeContainer::KillAllEntities()
{
	std::vector<Centipede>::iterator iter;
	for( iter = Centipedes.begin(); iter != Centipedes.end(); ++iter )
	{
		if( iter->Active )
		{
			iter->Active = false;
			iter->x = 600 << 12;
			iter->y = 96 << 12;
		}
	}
	
}

//--------------------------------------
//	Non-Virtual Functions
//--------------------------------------
void CentipedeContainer::Spawn( int _x, int _y )
{
	bool SlotFull = true;      		// value to check if all the slots are occupied
	
	std::vector<Centipede>::iterator iter;
	for( iter = Centipedes.begin(); iter != Centipedes.end(); ++iter )
	{
		if( !iter->Active )		// insert a bullet on a free slot
		{
			iter->x = _x;
			iter->y = _y;
			iter->Active = true;
			iter->Frame = 0;
			iter->Counter = 0;
			iter->Hp = 128;
			iter->LoadPath( 300, 96, -500, 96 );
			iter->ResetSegments();
			SlotFull = false;			// there's a free slot so we dont execute the code below
			break;
		
		}
	}
	
	// All the slots are full
	// Only push back a bullet if the slot is full and we have less than numbullets
	if( SlotFull && (Centipedes.size() < MAX_CENTIPEDES) )
	{
		Centipede centipede;
		centipede.x = _x;
		centipede.y = _y;
		centipede.Active = true;
		centipede.Frame = 0;
		centipede.Counter = 0;
		centipede.Hp = 128;
		centipede.LoadPath( 300, 96, -500, 96 );
		centipede.ResetSegments();
		Centipedes.push_back(centipede);
	}
	
}


	
