/*
	Asteroids
    Copyright (C) 2006  Darragh Coy.

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

/*##############################################################################

Module:   Asteroid.cpp
Purpose:  Asteroid drawing, movement and initialisation

##############################################################################*/

#include "Asteroid.h"

/*******************************************************************************
Private module prototypes: Not for outside use!
*******************************************************************************/
   
   // Make a new asteroid     
   
   void Asteroid_Create         (GameData_t * gamedata );                   
   
   // Check asteroid-asteroid collisions
   
   void Asteroid_CollisionCheck (GameData_t * gamedata , Asteroid_t *  asteroid);    

/*******************************************************************************
Variables
*******************************************************************************/
   
   // Little enum to describe where the asteroid is to be spawned
   
   enum SpawnPos 
   {
        SPAWN_TOP     = 0  ,
        SPAWN_LEFT         ,
        SPAWN_BOTTOM       ,
        SPAWN_RIGHT        ,
        SPAWN_ANYWHERE
   };
   
   // Time (in seconds) since last asteroid was spawned
   
   long double timeSinceSpawn = 0;

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

Name:       Asteroid_Init()
Purpose:    Initialises the asteroids and creates a few
Parameters: Pointer to Gamedata
Returns:    none

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

void Asteroid_Init ( GameData_t * gamedata )
{
   // Set random seed based on current time
   
   srand(clock());
     
   // Clear the asteroid array data

   memset( gamedata->asteroids , 0 , sizeof gamedata->asteroids );
   
   // Create a certain number of random asteroids
   
   for ( int i = 0 ; i < AST_INIT_ASTEROIDS ; i++ )
   {
       // Create an asteroid
       
       Asteroid_Create(gamedata);
   }
   
   // Set time since last asteroid was spawned as 0
   
   timeSinceSpawn = 0;
   
}

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

Name:       Asteroid_Create()
Purpose:    Creates a single random asteroid in a position just offscreen
Parameters: Pointer to Asteroid array
Returns:    none

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

void Asteroid_Create ( struct GameData_t * gamedata )
{
     /**************************************************************************
     Set random seed based on current system clock time
     **************************************************************************/     
     
     // Get current clock time 
     
     LARGE_INTEGER clocktime; QueryPerformanceCounter(&clocktime);
     
     // Set random seed
     
     srand(clocktime.LowPart);
     
     /**************************************************************************
     find free position in bottom half of asteroid array to spawn
     **************************************************************************/
     
     Asteroid_t * asteroids = gamedata->asteroids; int freepos = -1;
     
     for ( int i = 0 ; i < AST_MAX_ASTEROIDS ; i++ )
     {
         // Check if asteroid is alive
         
         if ( ! asteroids[i].asteroidAlive )
         {
            // Not alive: found a free place
            
            freepos = i; break;
         } 
     }
     
     /**************************************************************************
     If no free position found then abort: cannot create another asteroid.
     **************************************************************************/     
     
     if ( freepos < 0 ) return;
     
     /**************************************************************************
     Get pointer to asteroid to create. Set asteroid as active and as a full
     asteroid.
     **************************************************************************/      
     
     Asteroid_t * pAsteroid = &(asteroids[freepos]);
     
     pAsteroid->asteroidAlive  = true;
     pAsteroid->asteroidHalf   = false;     
     
     /**************************************************************************
     Decide on asteroid spwan position: which side offscreen will it be ?
     **************************************************************************/        
     
     SpawnPos spawnPos = (SpawnPos)( rand() % SPAWN_ANYWHERE );
     
     /**************************************************************************
     Pick center position of the asteroid on one side offscreen
     **************************************************************************/  
     
     switch ( spawnPos )
     {
        /***********************************************************************
         Spawn at top of screen
        ***********************************************************************/
        
        case SPAWN_TOP:
        {
             // Set center position
             
             pAsteroid->center.x = float( rand() % WINDOW_X );
             pAsteroid->center.y = -AST_MAX_RADIUS;
             
             // Decide on move angle:
             
             float angle = float( 270 + ( rand() % 90 - 45 ) );
             
             // Convert angle to radians
             
             angle *= TO_RADIANS;
             
             // Create movement vector based on this angle and minimum asteroid speed
             
             pAsteroid->velocity.x =  cos(angle) * AST_MIN_SPEED;
             pAsteroid->velocity.y = -sin(angle) * AST_MIN_SPEED;             
             
        }break;
        
        /***********************************************************************
         Spawn at bottom of screen
        ***********************************************************************/        
        
        case SPAWN_BOTTOM:
        {
             // Set center position
             
             pAsteroid->center.x = float( rand() % WINDOW_X );
             pAsteroid->center.y = WINDOW_Y + AST_MAX_RADIUS;
             
             // Decide on move angle:
             
             float angle = float( 90 + ( rand() % 90 - 45 ) );
             
             // Convert angle to radians
             
             angle *= TO_RADIANS;
             
             // Create movement vector based on this angle and minimum asteroid speed
             
             pAsteroid->velocity.x =  cos(angle) * AST_MIN_SPEED;
             pAsteroid->velocity.y = -sin(angle) * AST_MIN_SPEED;             
             
        }break;    
        
        /***********************************************************************
         Spawn at left of screen
        ***********************************************************************/        
        
        case SPAWN_LEFT:
        {
             // Set center position
             
             pAsteroid->center.x = - AST_MAX_RADIUS;
             pAsteroid->center.y = float( rand() % WINDOW_Y );
             
             // Decide on move angle:
             
             float angle = float( 0 + ( rand() % 90 - 45 ) );
             
             // Convert angle to radians
             
             angle *= TO_RADIANS;
             
             // Create movement vector based on this angle and minimum asteroid speed
             
             pAsteroid->velocity.x =  cos(angle) * AST_MIN_SPEED;
             pAsteroid->velocity.y = -sin(angle) * AST_MIN_SPEED;             
             
        }break;
        
        /***********************************************************************
         Spawn at right of screen
        ***********************************************************************/        
        
        case SPAWN_RIGHT:
        {
             // Set center position
             
             pAsteroid->center.x = WINDOW_X + AST_MAX_RADIUS;
             pAsteroid->center.y = float( rand() % WINDOW_Y );
             
             // Decide on move angle:
             
             float angle = float( 180 + ( rand() % 90 - 45 ) );
             
             // Convert angle to radians
             
             angle *= TO_RADIANS;
             
             // Create movement vector based on this angle and minimum asteroid speed
             
             pAsteroid->velocity.x =  cos(angle) * AST_MIN_SPEED;
             pAsteroid->velocity.y = -sin(angle) * AST_MIN_SPEED;             
             
        }break;                                
        
     } // end switch (spawnpos) 
     
     /**************************************************************************
     Check if the asteroid is within collision of any other asteroids: if so
     then abort asteroid creation...
     **************************************************************************/     
     
     // Check all asteroids
     
     for ( int i = 0 ; i < AST_MAX_ASTEROIDS * 2 ; i ++ )
     {
         // Get pointer to current asteroid
         
         Asteroid_t * pCheckAsteroid = gamedata->asteroids + i;
         
         // If asteroid inactive then don't check..
         
         if ( ! pAsteroid->asteroidAlive ) continue;
         
         // If same asteroid as one being created then don't check
         
         if ( pAsteroid == pCheckAsteroid ) continue;
         
         // Get distance from the center of the asteroid being created to the
         // check asteroid
         
         float dist = Vec2D_Distance( &pCheckAsteroid->center , &pAsteroid->center );
         
         // If distance < twice maximum radius of an asteroid then abort function. This may not
         // neccessarily mean that the two asteroids are in collision, but its
         // a good idea to leave some additional headroom between asteroids when
         // creating them
         
         if ( dist < AST_MAX_RADIUS * 2 )
         {
              // Too close to the check asteroid. Set the asteroid being created
              // as 'dead' again
              
              pAsteroid->asteroidAlive = false;
              
              // Abort the function altogehter. Do no more creation
              
              return;
         }
     
     }
     
     /**************************************************************************
     Adjust asteroid speed based on player score. The higher the score the 
     harder things become...
     **************************************************************************/
     
     pAsteroid->velocity.x *= powf( float(gamedata->player.score + 1) , AST_SCORE_SPEED_EXPONENT);
     pAsteroid->velocity.y *= powf( float(gamedata->player.score + 1) , AST_SCORE_SPEED_EXPONENT);    
     
     /**************************************************************************
     Create a random set of points for the asteroid
     **************************************************************************/      
     
     // Create the points
     
     for ( int pointNum = 0 ; pointNum < AST_NUM_POINTS ; pointNum++ )
     {
         // Get angle along circle radius to create point
     
         float angle =  ( 359.0f / (float)AST_NUM_POINTS ) * float( pointNum );
         
         // Get a random float between 0.0 - 1.0
         
         float randFloat = float(rand() % 65536) / 65535.0f;
         
         // Use this random float to pick a random distance between min and
         // maximum radius sizes
         
         float dist = AST_MIN_RADIUS * randFloat + (1.0f-randFloat) * AST_MAX_RADIUS;
         
         // Now set the position of the point using this information
         
         pAsteroid->points[pointNum].x = pAsteroid->center.x + cos(angle * TO_RADIANS ) * dist;
         pAsteroid->points[pointNum].y = pAsteroid->center.y + sin(angle * TO_RADIANS ) * dist;
     }
     
     /**************************************************************************
     Set bounding sphere and center point of new asteroid:
     **************************************************************************/      
     
     Asteroid_SetBoundingSphere(pAsteroid);
     
     /**************************************************************************
     Pick one of the asteroid points to rotate about
     **************************************************************************/       
     
     int rotatePoint = rand() % AST_NUM_POINTS;
     
     pAsteroid->rotationCenter.x = pAsteroid->points[rotatePoint].x;
     pAsteroid->rotationCenter.y = pAsteroid->points[rotatePoint].y;
     
     /**************************************************************************
     Decide on a random rotation amount either clockwise or counter clockwise
     **************************************************************************/ 
     
     // Get a random float between 0.0 - 1.0
         
     float randFloat =  (float)(rand() % 65536) / 65535.0f;  
     
     // Pick rotation amount between minimum and maximum
     
     pAsteroid->rotationAmt = AST_MIN_ROTATE_SPEED * randFloat +  (1.0f-randFloat) * AST_MAX_ROTATE_SPEED;
     
     // Get the next random number and if odd then negate the rotation amount
     
     if ( rand() & 1 ){ pAsteroid->rotationAmt *= -1; }
}

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

Name:       Asteroid_Move()
Purpose:    Moves and rotates all active asteroids. May also create a asteroid every
            so often
Parameters: Pointer to Gamedata
Returns:    none

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

void Asteroid_Move ( struct GameData_t * gamedata )
{
     /**************************************************************************
     If time since last asteroid spawn has passed minimum time required for spawn
     based on current players score then spawn an asteroid:
     **************************************************************************/     
     
     if ( float(timeSinceSpawn) > AST_MIN_SPAWN_FREQ / powf(float(gamedata->player.score+1),AST_SCORE_FREQ_EXPONENT) )
     {
          // Passed alloted time to make an asteroid for this difficulty level. Make one:
                    
          Asteroid_Create ( gamedata );
          
          // Set time elapsed since last spawn to 0
          
          timeSinceSpawn = 0;
     }
     else
     {
          // Not enough time has passed to create an asteroid: add last
          // frame time to time since last spawn
          
          timeSinceSpawn += Timer_FrameTime();
     }
     
     /**************************************************************************
     Get moving all of the asteroids
     **************************************************************************/
     
     // Pointer to the current asteroid
     
     Asteroid_t * pAsteroid = gamedata->asteroids;
     
     // Move all active asteroids

     for ( int i = 0 ; i < AST_MAX_ASTEROIDS * 2 ; i++ )
     {
         /**********************************************************************
          Move and rotate the asteroid if alive
         ***********************************************************************/
         
         if ( pAsteroid->asteroidAlive )
         {
              // Asteroid is active-> decide whether to move and rotate all or half the points    
              
              if ( pAsteroid->asteroidHalf )
              {
                   // Half asteroid: move and rotate half the points
                   
                   for ( int j = 0 ; j < AST_NUM_POINTS / 2 ; j++ )
                   {
                       // Rotate this point about the rotate point
                       
                       Vec2D_Rot
                          ( 
                          &pAsteroid->points[j]      ,
                          &pAsteroid->rotationCenter ,
                          pAsteroid->rotationAmt * Timer_FrameTime()
                          );
                       
                       // move asteroid by movement vector
                       
                       pAsteroid->points[j].x += pAsteroid->velocity.x * Timer_FrameTime();
                       pAsteroid->points[j].y += pAsteroid->velocity.y * Timer_FrameTime();                                        
                   }
              }
              else
              {
                   // Full asteroid: move all points
                  
                   for ( int j = 0 ; j < AST_NUM_POINTS ; j++ )
                   {
                       // Rotate this point about the rotate point
                       
                       Vec2D_Rot
                          ( 
                          &pAsteroid->points[j]      ,
                          &pAsteroid->rotationCenter ,
                          pAsteroid->rotationAmt * Timer_FrameTime()
                          );                       
                       
                       // move asteroid by movement vector
                       
                       pAsteroid->points[j].x += pAsteroid->velocity.x * Timer_FrameTime();
                       pAsteroid->points[j].y += pAsteroid->velocity.y * Timer_FrameTime();                            
                   }                  
                   
              } // endif asteroid is half asteroid    
              
              // Move and rotate asteroid center point: used for telling whether
              // offscreen- important that it stays right!
              
              Vec2D_Rot
                 ( 
                 &pAsteroid->center         ,
                 &pAsteroid->rotationCenter ,
                 pAsteroid->rotationAmt * Timer_FrameTime()
                 );
                                     
              pAsteroid->center.x += pAsteroid->velocity.x * Timer_FrameTime();
              pAsteroid->center.y += pAsteroid->velocity.y * Timer_FrameTime();              
              
              // Move the rotation point by the movement amount
              
              pAsteroid->rotationCenter.x += pAsteroid->velocity.x * Timer_FrameTime();
              pAsteroid->rotationCenter.y += pAsteroid->velocity.y * Timer_FrameTime();
              
              /*****************************************************************
              Now if the asteroid goes offscreen then mark it as inactive
              *****************************************************************/
              
                 // Check against left side of screen
                 
                 if ( pAsteroid->center.x < -AST_OFFSCREEN_DIST )
                 { 
                      // offscreen: mark as inactive
                      
                      pAsteroid->asteroidAlive = false;
                 }
                 
                 // Check against right side of screen
                 
                 if ( pAsteroid->center.x > WINDOW_X + AST_OFFSCREEN_DIST )
                 { 
                      // offscreen: mark as inactive
                      
                      pAsteroid->asteroidAlive = false;
                 }
                 
                 // Check against top side of screen
                 
                 if ( pAsteroid->center.y < -AST_OFFSCREEN_DIST )
                 { 
                      // offscreen: mark as inactive
                      
                      pAsteroid->asteroidAlive = false;
                 }  
                 
                 // Check against bottom side of screen
                 
                 if ( pAsteroid->center.y > WINDOW_Y + AST_OFFSCREEN_DIST )
                 { 
                      // offscreen: mark as inactive
                      
                      pAsteroid->asteroidAlive = false;
                 }               
                 
            /*******************************************************************
            Do collision checks for this asteroid with the other asteroids
            *******************************************************************/             
            
            Asteroid_CollisionCheck(gamedata,pAsteroid);

         } //endif asteroid alive

         /**********************************************************************
          Move onto the next asteroid
         **********************************************************************/
         
         pAsteroid++;     
         
     } // end asteroid move/rotate loop
}

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

Name:       Asteroid_CollisionCheck()
Purpose:    Checks for collision with other asteroids and reverses move direction
            if a collision occurs
Parameters: Pointer to Gamedata , asteroid to check
Returns:    none

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

void Asteroid_CollisionCheck ( GameData_t * pGameData , Asteroid_t * checkAsteroid )
{
     // Get pointer to asteroid array
     
     Asteroid_t * pAsteroids = pGameData->asteroids;
     
     // Check against all asteroids in the array ( except this one! )
     
     for ( int i = 0; i < AST_MAX_ASTEROIDS * 2 ; i++ )
     {
         // If this asteroid is same as check asteroid then move on
         
         if ( pAsteroids == checkAsteroid ){ pAsteroids++; continue; }
         
         // If this asteroid is inactive then move on
         
         if ( ! pAsteroids->asteroidAlive ){ pAsteroids++; continue; }
         
         // Get the distance between the current asteroid and the check asteroid
         
         float dist = Vec2D_Distance(&pAsteroids->center,&checkAsteroid->center);
         
         // Get the sum of the bounding sphere radii for the two asteroids
         // This is the minimum distance the two asteroids should be apart without
         // a collision happening:
         
         float minDistance = pAsteroids->collisionRadius + checkAsteroid->collisionRadius;
         
         // If distance is less than or equal to the sum of the bounding sphere radii between
         // the two asteroids then they are in collision
         
         if ( dist < minDistance )
         {
              // Two asteroids are in collision. Get a vector going from the center
              // of the asteroid the check asteroid is colliding with to the center
              // of the check asteroid. 
              
              Vec2D_t moveVec; 
              
              moveVec.x = checkAsteroid->center.x - pAsteroids->center.x;
              moveVec.y = checkAsteroid->center.y - pAsteroids->center.y; 
              
              // Normalise this vector
              
              Vec2D_Normalise(&moveVec);
              
              // Now scale the vector by the amount the two bounding circles intersect.
              // This vector will be used to move the check asteroid back into a position
              // where it is not in collision with the other asteroid:
                       
              moveVec.x *= ( minDistance - dist );
              moveVec.y *= ( minDistance - dist );
              
              // Now move all of the points of the check asteroid back into
              // a position where they do not collide with the other asteroid.
              // Forget not to move the center of rotation as well as the actual
              // center because if these aren't moved then things will start going wrong..
              
              if ( checkAsteroid->asteroidHalf )
              {
                  // Half asteroid move only half the points
                     
                  for ( int i = 0 ; i < AST_NUM_POINTS / 2 ; i++ )
                  {
                      checkAsteroid->points[i].x += moveVec.x;                              
                      checkAsteroid->points[i].y += moveVec.y;
                  }    
                  
              }             
              else
              {
                  // Full asteroid: move all points
                  
                  for ( int i = 0 ; i < AST_NUM_POINTS  ; i++ )
                  {
                      checkAsteroid->points[i].x += moveVec.x;                              
                      checkAsteroid->points[i].y += moveVec.y;
                  }   
              }                    
              
              checkAsteroid->rotationCenter.x += moveVec.x;   
              checkAsteroid->rotationCenter.y += moveVec.y;
              checkAsteroid->center.x         += moveVec.x;   
              checkAsteroid->center.y         += moveVec.y;   
              
              // This is a hacky and not entirely correct way of getting the 
              // asteroids to 'bounce' off each other. Simply swap the movement
              // vectors between them so they set off in opposite (usually) directions.
              // Looks good in most cases- there's no point in going into accurate physics
              // or anything for this simple collision:
                    
              Vec2D_t temp;
              
              temp.x = pAsteroids->velocity.x; 
              temp.y = pAsteroids->velocity.y; 
              
              checkAsteroid->velocity.x = temp.x;           
              checkAsteroid->velocity.y = temp.y;                                       
         }
  
         // Move onto the next asteroid
         
         pAsteroids++;
         
     } // end of check against all other asteroids loop
}

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

Name:       Asteroid_Shift()
Purpose:    Shifts all active asteroids by a given amount
Parameters: Pointer to Gamedata , x shift, y shift
Returns:    none

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

void Asteroid_Shift ( struct GameData_t * gamedata , float x , float y)
{
     // Pointer to the current asteroid
     
     Asteroid_t * pAsteroid = gamedata->asteroids;
     
     // Shift all active asteroids

     for ( int i = 0 ; i < AST_MAX_ASTEROIDS * 2 ; i++ )
     {
         // If this asteroid is active then move it
         
         if ( pAsteroid->asteroidAlive )
         {
              // Asteroid is active-> decide whether to move all or half the points
              
              if ( pAsteroid->asteroidHalf )
              {
                   // Half asteroid: move half the points
                   
                   for ( int j = 0 ; j < AST_NUM_POINTS / 2 ; j++ )
                   {
                       // move this point by given amount
                       
                       pAsteroid->points[j].x += x;
                       pAsteroid->points[j].y += y;                                          
                   }
              }
              else
              {
                  // Full asteroid: move all points
                  
                   for ( int j = 0 ; j < AST_NUM_POINTS ; j++ )
                   {
                       // move this point by given amount
                       
                       pAsteroid->points[j].x += x;
                       pAsteroid->points[j].y += y;                       
                   }                  
              }
              
              // Move center and rotation points of the asteroid
              
              pAsteroid->center.x += x;
              pAsteroid->center.y += y;
              pAsteroid->rotationCenter.x += x;
              pAsteroid->rotationCenter.y += y;  
          
         } // endif asteroid alive
         
         // Move onto the next asteroid
         
         pAsteroid++;
         
     } // end move asteroids loop
}

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

Name:       Asteroid_Inside()
Purpose:    Tells if a point is inside a given asteroid.
Parameters: Pointer to a point, and a pointer to an asteroid
Returns:    True if the point is inside the asteroid- false if not..

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

bool Asteroid_Inside ( Vec2D_t * point , Asteroid_t * asteroid )
{
   /****************************************************************************
   In order for this test to work properly the asteroid polygon must be broken down
   into individual triangles each which has one point at its center. As i have
   found out through trial and error, testing any arbitrary shape does not work
   as intended in some cases because of the nature of the test. (tests which side of a line is the point on ? )
   However it is guaranted to work if the area of the asteroid is split up into 
   triangles and if we test if the point is within each triangle. 
   
   First check is to see whether the asteroid is a half asteroid or full asteroid:
   ****************************************************************************/      
   
   // Use static variable to hold test triangle: faster because no pushing or
   // popping of the stack!
   
   static Vec2D_t testTri[3];
   
   // See if half or full asteroid:
   
   if ( asteroid->asteroidHalf )
   {
      /*************************************************************************
      Half asteroid: check half the amount of triangles
      *************************************************************************/
      
      for ( int i = 0; i < AST_NUM_POINTS / 2 ; i += 1 )
      {
          // Makeup check triangle using two points from the asteroid and the
          // center point. Use modulus to ensure that we don't go over the 
          // array bounds!
          
          testTri[0].x = asteroid->points[i].x;
          testTri[0].y = asteroid->points[i].y;
          
          testTri[1].x = asteroid->points[ (i+1) % (AST_NUM_POINTS/2) ].x;          
          testTri[1].y = asteroid->points[ (i+1) % (AST_NUM_POINTS/2) ].y;   
          
          testTri[2].x = asteroid->center.x;          
          testTri[2].y = asteroid->center.y;                    
          
          // Check if the point is inside the triangle: return true if it is
          
          if ( Vec2D_InsideTri(point,testTri) ) return true;
      }
   
   }
   else
   {
      /*************************************************************************
      Full asteroid: check all the triangles 
      *************************************************************************/   
   
      for ( int i = 0; i < AST_NUM_POINTS  ; i += 1 )
      {
          // Makeup check triangle using two points from the asteroid and the
          // center point. Use modulus to ensure that we don't go over the 
          // array bounds!
          
          testTri[0].x = asteroid->points[i].x;
          testTri[0].y = asteroid->points[i].y;
          
          testTri[1].x = asteroid->points[ (i+1) % AST_NUM_POINTS ].x;          
          testTri[1].y = asteroid->points[ (i+1) % AST_NUM_POINTS ].y;   
          
          testTri[2].x = asteroid->center.x;          
          testTri[2].y = asteroid->center.y;                    
          
          // Check if the point is inside the triangle: return true if it is
          
          if ( Vec2D_InsideTri(point,testTri) ) return true;      
      }   
   
   }

   /****************************************************************************
   If we've gotten this far then the test has failed. The point is not inside
   the asteroid.. Return false:
   ****************************************************************************/
   
   return false;
}

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

Name:       Asteroid_Draw()
Purpose:    Draws all active asteroids
Parameters: Pointer to Gamedata
Returns:    none

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

void Asteroid_Draw ( struct GameData_t * gamedata )
{
     // Get pointer to start of asteroid array
     
     Asteroid_t * pAsteroids = gamedata->asteroids;
     
     // Draw all the active asteroids
     
     for ( int i = 0 ; i < AST_MAX_ASTEROIDS * 2 ; i++ )
     {
         // If the asteroid is active then draw it...
         
         if ( pAsteroids->asteroidAlive )
         {
            // Begin drawing 
            
            glBegin(GL_LINE_LOOP);
            
               // Set draw color
               
               glColor4f( 1.0 , 1.0 , 1.0 , 1.0 );
               
               // If half asteroid draw only half the points: otherwise
               // draw the full amount
               
               if ( pAsteroids->asteroidHalf )
               {
                    // Half asteroid: draw half amount of lines
                    
                    for ( int j = 0 ; j < AST_NUM_POINTS / 2 ; j++ )
                    {
                        // Pass this vertex to OpenGL
                        
                        glVertex2f
                            ( 
                            pAsteroids->points[j].x , 
                            pAsteroids->points[j].y 
                            );                   
                    }
               }
               else
               {
                    // Full asteroid: draw all the lines
                 
                    for ( int j = 0 ; j < AST_NUM_POINTS  ; j++ )
                    {
                        // Pass this vertex to OpenGL
                        
                        glVertex2f
                            ( 
                            pAsteroids->points[j].x , 
                            pAsteroids->points[j].y 
                            );                            
                    }                 
               }
               
            // End drawing
            
            glEnd();
            
         } // endif asteroid alive
         
         // Move onto the next asteroid
         
         pAsteroids++;
         
     } // end of asteroid draw loop
}

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

Name:       Asteroid_SetBoundingSphere()
Purpose:    Sets the bounding sphere and center of a given asteroid based on
            its points
Parameters: Pointer to an asteroid
Returns:    none

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

void Asteroid_SetBoundingSphere( Asteroid_t * asteroid )
{
     // Calculate Center of asteroid: if half asteroid then only consider half
     // the amount of points:
            
     asteroid->center.x = 0;               
     asteroid->center.y = 0; 
     
     if ( asteroid->asteroidHalf )
     {
          // Half asteroid
          
          for ( int i = 0 ; i < AST_NUM_POINTS / 2 ; i++ )
          {
              asteroid->center.x += asteroid->points[i].x;
              asteroid->center.y += asteroid->points[i].y;          
          }
          
          asteroid->center.x /= AST_NUM_POINTS / 2;
          asteroid->center.y /= AST_NUM_POINTS / 2;          
     }    
     else
     {
          // Full asteroid
          
          for ( int i = 0 ; i < AST_NUM_POINTS; i++ )
          {
              asteroid->center.x += asteroid->points[i].x;
              asteroid->center.y += asteroid->points[i].y;          
          }          
          
          asteroid->center.x /= AST_NUM_POINTS;
          asteroid->center.y /= AST_NUM_POINTS;               
     }
     
     // Calculate maximum distance from center to any point: this will be
     // bounding spehre radius..
     
     asteroid->collisionRadius = 0;
     
     if ( asteroid->asteroidHalf )
     {
          // Half asteroid
          
          for ( int i = 0 ; i < AST_NUM_POINTS / 2 ; i++ )
          {
              // Get distance from center to this point
              
              float dist =  Vec2D_Distance(&asteroid->center,&asteroid->points[i]);
              
              // If distance > current collision radius then save: 
                    
              if ( dist > asteroid->collisionRadius )
              {
                   // Distance is greater than collision radius. Save new bounding
                   // sphere radius..
                   
                   asteroid->collisionRadius = dist;
              }
          }
          
     }
     else
     {
          // Full asteroid
     
          for ( int i = 0 ; i < AST_NUM_POINTS; i++ )
          {
              // Get distance from center to this point
              
              float dist =  Vec2D_Distance(&asteroid->center,&asteroid->points[i]);
              
              // If distance > current collision radius then save: 
                    
              if ( dist > asteroid->collisionRadius )
              {
                   // Distance is greater than collision radius. Save new bounding
                   // sphere radius..
                   
                   asteroid->collisionRadius = dist;
              }        
          }     
     
     } // end else of if(asteroid->asteroidHalf)
     
}

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

Name:       Asteroid_Split()
Purpose:    Tries to split up a given asteroid into two halves
Parameters: Pointer to Gamedata, pointer to asteroid
Returns:    none

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

void Asteroid_Split ( GameData_t * gamedata , Asteroid_t * asteroid )
{
     // Set split asteroid as a half asteroid
     
     asteroid->asteroidHalf = true;
     
     // Calculate new center and bounding sphere radius of half asteroid:
                  
     Asteroid_SetBoundingSphere(asteroid);
     
     // Pointer to second half of asteroid array
     
     Asteroid_t * pNewAsteroid = gamedata->asteroids + AST_MAX_ASTEROIDS;
     
     // Search in extra part of asteroid array for a free slot to make another
     // asteroid:
                 
     int freePos = -1;

     for ( int i = AST_MAX_ASTEROIDS ; i < AST_MAX_ASTEROIDS * 2 ; i++ )
     {
         // See if this asteroid is active:
                
         if ( ! pNewAsteroid->asteroidAlive )
         {
              // Asteroid not alive: use this slot to make a new asteroid...
              
              freePos = i ; break;
         }    
         
         // Move onto next asteroid
         
         pNewAsteroid++;
     }    
     
     // If haven't found a free slot in the second half of the asteroid array
     // to make a new asteroid then abort creating a new asteroid...
     
     if ( freePos < 0 ) return;
     
     // Set the second half of the old asteroid's points as the points of the
     // new half asteroid:
            
     memcpy( 
             pNewAsteroid->points ,
             asteroid->points + ( AST_NUM_POINTS / 2 ),             
             sizeof(Vec2D_t)  * ( AST_NUM_POINTS / 2 )
            );
            
     // Set new asteroid as active and as a half asteroid
     
     pNewAsteroid->asteroidAlive = true;
     pNewAsteroid->asteroidHalf  = true;              
            
     // Calculate bounding sphere and center of new asteroid:
                  
     Asteroid_SetBoundingSphere(pNewAsteroid);    
     
     // Set new asteroids velocity as reverse of other split asteroid:
            
     pNewAsteroid->velocity.x = -asteroid->velocity.x;
     pNewAsteroid->velocity.y = -asteroid->velocity.y;
     
     // Set new asteroids rotation amount as reverse of old asteroid
     
     pNewAsteroid->rotationAmt = -asteroid->rotationAmt;
     
     // Pick a random rotation point for the new asteroid:
     
     int randomPoint = rand() % ( AST_NUM_POINTS / 2 );
     
     pNewAsteroid->rotationCenter.x = pNewAsteroid->points[randomPoint].x;
     pNewAsteroid->rotationCenter.y = pNewAsteroid->points[randomPoint].y;     
}
