#include "config.h"

#include "allegro.h"

#include "globvars.h"

#include "stuff.h"
#include "enemy.h"
#include "cloud.h"

#include "palette.h"

#include <math.h>

void run_basic_pbullet(int w, int b);
void run_multi_pbullet(int w, int b);
void destroy_pbullet(int w, int b);
int pbullet_collision(int w, int b, int survive);
int seeker_collision(int w, int s);
void pbullet_explodes(int w, int b);
void run_circle_pbullet(int w, int b);
void run_turret_pbullet(int w, int b);
void run_white2_pbullet(int w, int b);
void run_green2_vortex(int w, int b);
void run_green2_pbullet(int w, int b);


void run_seeker(int w, int s);
void find_new_seeker_target(int w, int s);
void destroy_seeker(int w, int s);
void run_wave(int w, int b);

void init_pbullets(void)
{

 int b, w;
 for (w = 0; w < 2; w ++)
 {
  for (b = 0; b < NO_PBULLETS; b ++)
  {
   pbullet[w][b].type = PBULLET_NONE;
  }
 }
 for (w = 0; w < 2; w ++)
 {
  for (b = 0; b < NO_SEEKERS; b ++)
  {
   seeker[w][b].active = 0;
  }
 }

}


int create_pbullet(int w, int type, int x, int y, int type2, int timeout)
{
  int b;

  for (b = 0; b < NO_PBULLETS; b ++)
  {
      if (pbullet[w][b].type == PBULLET_NONE)
       break;
      if (b == NO_PBULLETS - 1)
       return -1;

  }

 pbullet[w][b].type = type;
 pbullet[w][b].type2 = type2;
 pbullet[w][b].type3 = 0;
 pbullet[w][b].x = x;
 pbullet[w][b].y = y;
 pbullet[w][b].timeout = timeout;
 pbullet[w][b].xsize = 0;
 pbullet[w][b].ysize = 0;
 switch(type)
 {
  case PBULLET_WAVE:
   pbullet[w][b].xsize = (20 + ((type2 - 250) / 30) / 3) * GRAIN;
   break;

 }
 // pbullet[w][b].player = player;
//bullet[w][b].pole = pole;

 return b;

}



void run_pbullets(void)
{
  int b, w;

  for (w = 0; w < 2; w ++)
  {
   for (b = 0; b < NO_PBULLETS; b ++)
   {
    switch(pbullet[w][b].type)
    {
        case PBULLET_NONE:
         continue;
        case PBULLET_BASIC:
        case PBULLET_HEAVY:
         run_basic_pbullet(w, b);
         break;
        case PBULLET_WAVE:
         run_wave(w, b);
         break;
        case PBULLET_MULTI1:
        case PBULLET_MULTI2:
        case PBULLET_MULTI3:
        case PBULLET_MULTI4:
        case PBULLET_MULTI5:
         run_multi_pbullet(w, b);
         break;
        case PBULLET_CIRCLE:
         run_circle_pbullet(w, b);
         break;
        case PBULLET_TURRET:
         run_turret_pbullet(w, b);
         break;
        case PBULLET_WHITE2:
         run_white2_pbullet(w, b);
         break;
        case PBULLET_GREEN2:
         run_green2_pbullet(w, b);
         break;
        case PBULLET_GREEN2_VORTEX:
         run_green2_vortex(w, b);
         break;
    }
   }
  }


  for (w = 0; w < 2; w ++)
  {
   for (b = 0; b < NO_SEEKERS; b ++)
   {
   if (seeker[w][b].active == 1)
    run_seeker(w, b);
   }
  }
}

void run_basic_pbullet(int w, int b)
{

    pbullet[w][b].y -= 16000;
    if (pbullet_collision(w, b, 0) == 1)
     return;

    if (pbullet[w][b].y <= -10000)
     destroy_pbullet(w, b);

}

void run_green2_pbullet(int w, int b)
{

    pbullet[w][b].y -= 13000;
    if (pbullet_collision(w, b, 2) == 1)
    {
     int b2 = create_pbullet(w, PBULLET_GREEN2_VORTEX, pbullet[w][b].x, pbullet[w][b].y, pbullet[w][b].type2, pbullet[w][b].type2 * 10 + 20);
     if (b2 != -1)
     {
      pbullet[w][b2].xsize = 10000;
      pbullet[w][b2].ysize = 10000;
     }
     create_cloud(w, CLOUD_EXPLODE, 1, pbullet[w][b].x, pbullet[w][b].y, 0, 0, 22 + grand(4));
     create_cloud(w, CLOUD_GREEN2_RING, 0, pbullet[w][b].x, pbullet[w][b].y, 0, 0, 25 + grand(10));
     destroy_pbullet(w, b);
     return;
    }

    create_cloud(w, CLOUD_GREEN2_RING, 0, pbullet[w][b].x + grand(4000) - grand(4000), pbullet[w][b].y + grand(4000) - grand(4000), 0, 0, 5 + grand(10));

    if (pbullet[w][b].y <= -10000)
     destroy_pbullet(w, b);

}

void run_green2_vortex(int w, int b)
{

//    pbullet[w][b].y -= 13000;
// size
//    if (pbullet_collision(w, b, 0) == 1)
//     return;
    pbullet_collision(w, b, 1);

    pbullet[w][b].timeout --;

    if (pbullet[w][b].timeout < 10)
    {
        pbullet[w][b].xsize = pbullet[w][b].timeout * 1000;
        pbullet[w][b].ysize = pbullet[w][b].timeout * 1000;
    }

    if (pbullet[w][b].timeout <= 0)
     destroy_pbullet(w, b);

//    create_cloud(w, CLOUD_GREEN2_RING, 0, pbullet[w][b].x + grand(5000) - grand(5000), pbullet[w][b].y + grand(5000) - grand(5000), 0, 0, 5 + grand(10));

//    if (pbullet[w][b].y <= -10000)
     //destroy_pbullet(w, b);

}



void run_white2_pbullet(int w, int b)
{

    pbullet[w][b].y -= pbullet[w][b].type2;
    if (pbullet_collision(w, b, 0) == 1)
     return;

    if (pbullet[w][b].y <= -10000)
     destroy_pbullet(w, b);

}

void run_multi_pbullet(int w, int b)
{

    pbullet[w][b].x += pbullet[w][b].x_speed;
    pbullet[w][b].y += pbullet[w][b].y_speed;
    if (pbullet_collision(w, b, 0) == 1)
     return;

    if (pbullet[w][b].y <= -10000)
     destroy_pbullet(w, b);

}

void run_turret_pbullet(int w, int b)
{

    pbullet[w][b].x += pbullet[w][b].x_speed;
    pbullet[w][b].y += pbullet[w][b].y_speed;
    if (pbullet_collision(w, b, 0) == 1)
     return;

    if (pbullet[w][b].y <= -10000)
     destroy_pbullet(w, b);

}

void run_circle_pbullet(int w, int b)
{

    pbullet[w][b].y -= 10000;
    pbullet[w][b].timeout --;
    int size = 1000 - pbullet[w][b].timeout;
    if (size > 20)
     size = 20;
    size *= (8 + pbullet[w][b].type2);
    size /= 8;
    size *= GRAIN;
    pbullet[w][b].xsize = size;
    pbullet[w][b].ysize = size;

    if (pbullet_collision(w, b, 0) == 1)
     return;

    if (pbullet[w][b].y <= -10000)
     destroy_pbullet(w, b);

}


void run_wave(int w, int b)
{

    pbullet[w][b].y -= 5000;
    pbullet_collision(w, b, 1);
    pbullet[w][b].timeout --;

    int c = 0;

    int pow = (pbullet[w][b].type2 - 250) / 30;

    if (pbullet[w][b].timeout % 4 == 0)
    {
     if (pbullet[w][b].type3 == 1)
     {
      c = create_cloud(w, CLOUD_PURPLE_WAVE, 10, pbullet[w][b].x, pbullet[w][b].y, 0, 0, 36 + pow + grand(30 + pow));
      pbullet[w][b].type3 = 0;
     }
      else
       c = create_cloud(w, CLOUD_BLUE_WAVE, 10, pbullet[w][b].x, pbullet[w][b].y, 0, 0, 36 + pow + grand(30 + pow));
    }
    if (pbullet[w][b].timeout % 4 == 2)
    {
     if (pbullet[w][b].type3 == 1)
     {
      c = create_cloud(w, CLOUD_RED_WAVE, 5, pbullet[w][b].x, pbullet[w][b].y, 0, 0, 10 + pow + grand(10 + pow));
      pbullet[w][b].type3 = 0;
     }
       else
        c = create_cloud(w, CLOUD_GREEN_WAVE, 5, pbullet[w][b].x, pbullet[w][b].y, 0, 0, 10 + pow + grand(10 + pow));
     if (c != -1)
     {
      cloud[w][c].delay = 2 + grand(3) + pow / 5;
    }
    }


    if (pbullet[w][b].y <= -10000)
     destroy_pbullet(w, b);

}



void destroy_pbullet(int w, int b)
{
  pbullet[w][b].type = PBULLET_NONE;
}


int pbullet_collision(int w, int b, int survive)
{
 int e;
 int destroy_type = 0;

 for (e = 0; e < NO_ENEMIES; e ++)
 {
  if (enemy[w][e].type == ENEMY_NONE)
   continue;
  if (pbullet[w][b].x >= enemy[w][e].x - eclass[enemy[w][e].type].size - pbullet[w][b].xsize
      && pbullet[w][b].x <= enemy[w][e].x + eclass[enemy[w][e].type].size + pbullet[w][b].xsize
      && pbullet[w][b].y >= enemy[w][e].y - eclass[enemy[w][e].type].size - pbullet[w][b].ysize
      && pbullet[w][b].y <= enemy[w][e].y + eclass[enemy[w][e].type].size + pbullet[w][b].ysize)
      {
//       if (hypot(pbullet[w][b].y - enemy[w][e].y, pbullet[w][b].x - pbullet[w][b].x) <= eclass[enemy[w][e].type].size)
//       if (hypot(pbullet[w][b].y - enemy[w][e].y, pbullet[w][b].x - enemy[w][e].x) <= eclass[enemy[w][e].type].size)
       {
/*        if (pbullet[w][b].type == PBULLET_GREEN2)
        {
         pbullet[w][b].type = PBULLET_GREEN2_VORTEX;
         pbullet[w][b].timeout = (pbullet[w][b].type2 * 5) + 10;
         return 1; // pbullet destroyed
        }*/
        destroy_type = pbullet_hits_enemy(w, b, e);
        if (pbullet[w][b].type == PBULLET_WAVE)
         pbullet[w][b].type3 = 1;
// create cloud according to destroy_type - eg POLE_WHITE, POLE_NONE etc
        if (survive == 2)
         return 1; // green2_circle
        if (survive == 0)
        {
         pbullet_explodes(w, b);
         destroy_pbullet(w, b);
         return 1; // bullet destroyed
        }
         else return 0; // bullet not destroyed
       }
      }

 }
 return 0; // bullet not destroyed
}


void blue_beam_collision(void)
{


 int e;
 int w = player.sides;

 int i, x, y, dam, which_explode;
 int j, max_y;

 switch(player.power [0])
 {
  case 1: dam = 12; which_explode = 0; break;
  case 2: dam = 15; which_explode = 0; break;
  case 3: dam = 18; which_explode = 1; break;
  case 4: dam = 21; which_explode = 2; break;
  case 5: dam = 24; which_explode = 2; break;
/*  case 1: dam = 5; which_explode = 0; break;
  case 2: dam = 7; which_explode = 0; break;
  case 3: dam = 9; which_explode = 1; break;
  case 4: dam = 11; which_explode = 2; break;
  case 5: dam = 13; which_explode = 2; break;*/
 }


 for (i = 0; i < 2; i ++)
 {
  if (i == 0)
   x = player.x - 5000 + abs(player.bank * 40);
    else
     x = player.x + 5000 - abs(player.bank * 40);

  y = player.y - 3000;

  for (e = 0; e < NO_ENEMIES; e ++)
  {
   if (enemy[w][e].type == ENEMY_NONE)
    continue;
   if (x >= enemy[w][e].x - eclass[enemy[w][e].type].size
       && x <= enemy[w][e].x + eclass[enemy[w][e].type].size
       && y >= enemy[w][e].y - eclass[enemy[w][e].type].size)
       {
         if (grand(2) == 0)
         {
         max_y = y;
         if (enemy[w][e].y + eclass[enemy[w][e].type].size - 10000 < max_y)
          max_y = enemy[w][e].y + eclass[enemy[w][e].type].size - 10000;

          for (j = enemy[w][e].y - eclass[enemy[w][e].type].size + 10000 + grand(10000); j < max_y; j += 5000 + grand(15000))
          {
           create_cloud(w, CLOUD_EXPLODE, which_explode, x, j, 0, 0, 3 + grand(3));


          }
         }
         hurt_enemy(w, e, dam, 0, 0);
//         esize = eclass[enemy[w][e].type].size] * 2;

         // remember, enemy might be destroyed here!!
         // so we can't assume its values are still good (actually we prob can, but let's not)
       }
  }
 }



}

void blue2_beam_collision(void)
{


 int e;
 int w = 0;
 if (player.sides == 0)
  w = 1;

 int i, x, y, dam, which_explode;
 int j, max_y;

 switch(player.power [1])
 {
  case 1: dam = 1200; which_explode = 0; break;
  case 2: dam = 1400; which_explode = 0; break;
  case 3: dam = 1600; which_explode = 1; break;
  case 4: dam = 1800; which_explode = 2; break;
  case 5: dam = 2000; which_explode = 2; break;
/*  case 1: dam = 5; which_explode = 0; break;
  case 2: dam = 7; which_explode = 0; break;
  case 3: dam = 9; which_explode = 1; break;
  case 4: dam = 11; which_explode = 2; break;
  case 5: dam = 13; which_explode = 2; break;*/
 }


  x = player.x;

  y = player.y - 5000;

  for (e = 0; e < NO_ENEMIES; e ++)
  {
   if (enemy[w][e].type == ENEMY_NONE)
    continue;
   if (x >= enemy[w][e].x - eclass[enemy[w][e].type].size
       && x <= enemy[w][e].x + eclass[enemy[w][e].type].size
       && y >= enemy[w][e].y - eclass[enemy[w][e].type].size)
       {
         max_y = y;
         if (enemy[w][e].y + eclass[enemy[w][e].type].size - 10000 < max_y)
          max_y = enemy[w][e].y + eclass[enemy[w][e].type].size - 10000;

          for (j = enemy[w][e].y - eclass[enemy[w][e].type].size + 10000 + grand(10000); j < max_y; j += 5000 + grand(15000))
          {
//           create_cloud(w, CLOUD_SLOW_EXPLODE, which_explode, x, j, 0, 0, 20 + grand(20));
           create_cloud(w, CLOUD_EXPLODE, which_explode, x, j, 0, 0, 8 + grand(8));

          }
         hurt_enemy(w, e, dam, 0, 0);
//         esize = eclass[enemy[w][e].type].size] * 2;

         // remember, enemy might be destroyed here!!
         // so we can't assume its values are still good (actually we prob can, but let's not)
       }

 }


 int zx, z2x = x, z2y = y, c;

 i = y;
 zx = x;

 create_cloud(w, CLOUD_EXPLODE, which_explode, x, y, 0, 0, 12);


 //for (i = y; i > 0; i -= 10000 + grand(8000))
 while(i > 0)
 {
   i -= 7000 + grand(5000);
   zx += grand(15000);
   zx -= grand(15000);
   if (zx < x - 20000)
    zx += grand(20000);
   if (zx > x + 20000)
    zx -= grand(20000);
   c = create_cloud(w, CLOUD_ZAP, player.power [1], zx, i, 0, 0, 12);//grand(2));
   if (c != -1)
   {
    cloud[w][c].x2 = z2x;
    cloud[w][c].y2 = z2y;
   }
   z2x = zx;
   z2y = i;
 }




}





void pbullet_explodes(int w, int b)
{
 int xa, i;

 switch(pbullet[w][b].type)
 {
      case PBULLET_BASIC:
       create_cloud(w, CLOUD_EXPLODE, 0, pbullet[w][b].x, pbullet[w][b].y, 0, 0, 10 + grand(5));
       break;
      case PBULLET_MULTI1:
      case PBULLET_MULTI2:
      case PBULLET_MULTI3:
      case PBULLET_MULTI4:
      case PBULLET_MULTI5:
       create_cloud(w, CLOUD_EXPLODE, 0, pbullet[w][b].x, pbullet[w][b].y, 0, 0, 6 + grand(4));
       break;
      case PBULLET_HEAVY:
       switch(pbullet[w][b].type2)
       {
        case 0:
        case 1: create_cloud(w, CLOUD_EXPLODE, 0, pbullet[w][b].x, pbullet[w][b].y, 0, 0, 15 + grand(10)); break;
        case 2:
        case 3: create_cloud(w, CLOUD_EXPLODE, 1, pbullet[w][b].x, pbullet[w][b].y, 0, 0, 15 + grand(10)); break;
        case 4:
        case 5: create_cloud(w, CLOUD_EXPLODE, 2, pbullet[w][b].x, pbullet[w][b].y, 0, 0, 15 + grand(10)); break;
       }
       break;
      case PBULLET_CIRCLE:
       create_cloud(w, CLOUD_GREEN_RING, pbullet[w][b].xsize, pbullet[w][b].x, pbullet[w][b].y, 0, 0, 16);
       for (i = 0; i < 4; i ++)
       {
        xa = grand(ANGLE_1);
        create_cloud(w, CLOUD_SLOW_EXPLODE, 1, pbullet[w][b].x + xpart(xa, pbullet[w][b].xsize), pbullet[w][b].y + ypart(xa, pbullet[w][b].xsize), 0, 0, 15 + grand(20));
// maybe slow_explode
       }
       break;
      case PBULLET_TURRET:
       create_cloud(w, CLOUD_EXPLODE, 2, pbullet[w][b].x, pbullet[w][b].y, 0, 0, 10 + grand(5));
       break;
      case PBULLET_WHITE2:
       create_cloud(w, CLOUD_EXPLODE, 2, pbullet[w][b].x, pbullet[w][b].y, 0, 0, 15 + grand(7));
       break;
 }

}

int seeker_collision(int w, int s)
{
 int e;
 int destroy_type = 0;

 for (e = 0; e < NO_ENEMIES; e ++)
 {
  if (enemy[w][e].type == ENEMY_NONE)
   continue;
  if (seeker[w][s].x >= enemy[w][e].x - eclass[enemy[w][e].type].size
      && seeker[w][s].x <= enemy[w][e].x + eclass[enemy[w][e].type].size
      && seeker[w][s].y >= enemy[w][e].y - eclass[enemy[w][e].type].size
      && seeker[w][s].y <= enemy[w][e].y + eclass[enemy[w][e].type].size)
      {
//       if (hypot(pbullet[w][b].y - enemy[w][e].y, pbullet[w][b].x - pbullet[w][b].x) <= eclass[enemy[w][e].type].size)
       if (hypot(seeker[w][s].y - enemy[w][e].y, seeker[w][s].x - enemy[w][e].x) <= eclass[enemy[w][e].type].size)
       {
        destroy_type = seeker_hits_enemy(w, s, e);
        create_cloud(w, CLOUD_DRAG_EXPLODE, 0, seeker[w][s].x, seeker[w][s].y, 0, 0, 30 + grand(5));
        // also see timeout
        destroy_seeker(w, s);
        return 1; // bullet destroyed
       }
      }

 }
 return 0; // bullet not destroyed
}


void create_seeker(int w, int angle, int target)
{

    int s;

    for (s = 0; s < NO_SEEKERS; s ++)
    {
        if (seeker[w][s].active == 0)
         break;
        if (s >= NO_SEEKERS - 1)
         return;
    }

  seeker[w][s].active = 1;
  seeker[w][s].x = player.x + xpart(angle, 5000);
  seeker[w][s].y = player.y + ypart(angle, 5000);

  create_cloud(w, CLOUD_SLOW_EXPLODE, 0, player.x + xpart(angle, 5000), player.y + ypart(angle, 5000), xpart(angle, 1000), ypart(angle, 1000), 25 + grand(8));

  seeker[w][s].angle = angle;
  seeker[w][s].x_speed = xpart(angle, 5000);
  seeker[w][s].y_speed = ypart(angle, 5000);
  seeker[w][s].target = target;
  //seeker[w][s].player = p;
  seeker[w][s].pole = player.pole;
  seeker[w][s].timeout = 160;

  seeker[w][s].acceleration = 700;
  seeker[w][s].drag = 970;
  seeker[w][s].turn = 450;

}

void run_seeker(int w, int s)
{

    int old_x = seeker[w][s].x;
    int old_y = seeker[w][s].y;
    int target_x = -1, target_y = -1;

    if (seeker[w][s].target == -1 && seeker[w][s].timeout % 5 == 0)
     find_new_seeker_target(w, s);


    if (seeker[w][s].target != -1)
    {
     target_x = enemy[w][seeker[w][s].target].x;
     target_y = enemy[w][seeker[w][s].target].y;
    }
     else
     {
      target_x = player.x;
      target_y = player.y;
     }


//    seeker[w][s].acceleration += 80;
    seeker[w][s].acceleration += 300;
    if (seeker[w][s].acceleration > 1900)
     seeker[w][s].acceleration = 1900;

    seeker[w][s].x_speed += xpart(seeker[w][s].angle, 600);
    seeker[w][s].y_speed += ypart(seeker[w][s].angle, 600);
    seeker[w][s].x += seeker[w][s].x_speed;
    seeker[w][s].y += seeker[w][s].y_speed;

    seeker[w][s].drag -= 50;
    if (seeker[w][s].drag < 900)
     seeker[w][s].drag = 900;

    seeker[w][s].x_speed *= 95;
    seeker[w][s].x_speed /= 100;
    seeker[w][s].y_speed *= 95;
    seeker[w][s].y_speed /= 100;

    int c = create_cloud(w, CLOUD_SEEKER_TRAIL, 0, seeker[w][s].x, seeker[w][s].y, 0, 0, 11);
    if (c != -1)
    {
     cloud[w][c].x2 = old_x;
     cloud[w][c].y2 = old_y;
     cloud[w][c].angle = seeker[w][s].angle;
    }

//    seeker[w][s].turn += 185;
//    if (seeker[w][s].turn > 1300)
     seeker[w][s].turn = 1300;
/* if (seeker[w][s].timeout < 130)
 {
    if (target_x != -1 || target_y != -1)
     seeker[w][s].angle = radians_to_angle(atan2((target_x - seeker[w][s].y), (target_y - seeker[w][s].x))) % ANGLE_1;

 }
  else*/
  {
    if (target_x != -1 || target_y != -1)
     seeker[w][s].angle = turn_towards_xy(seeker[w][s].x, seeker[w][s].y, target_x, target_y, seeker[w][s].angle, (int) seeker[w][s].turn / 10);
  }
//     seeker[s].angle = turn_towards_xy(seeker[s].x, seeker[s].y, target_x, target_y, seeker[s].angle, 32);

    seeker[w][s].timeout --;

    if (seeker_collision(w, s))
     return;

    if (seeker[w][s].timeout <= 0)
    {
     create_cloud(w, CLOUD_DRAG_EXPLODE, 0, seeker[w][s].x, seeker[w][s].y, 0, 0, 30 + grand(5)); // also in collision
     destroy_seeker(w, s);
    }

}

void find_new_seeker_target(int w, int s)
{

 int enemies_found = 0;


 int e;//, j;
 int x = seeker[w][s].x;
 int y = seeker[w][s].y;

 for (e = 0; e < NO_ENEMIES; e ++)
 {
  if (enemy[w][e].type == ENEMY_NONE)
  {
   enemy[w][e].distance = 700000;
   continue;
  }

  enemy[w][e].distance = hypot(enemy[w][e].y - y, enemy[w][e].x - x) / 100;
  enemies_found ++;

 }

 if (enemies_found == 0)
  return;

 int closest = -1;
 int smallest_distance = 700000;

  for (e = 0; e < NO_ENEMIES; e ++)
  {
   if (enemy[w][e].distance < smallest_distance)
   {
/*    for (j = 0; j < NO_SEEKERS; j ++)
    {
     if (seeker[w][j].target == e) // should allow multiple seekers to lock on to big targets
      continue;
    }*/
    closest = e;
    smallest_distance = enemy[w][e].distance;
   }
  }

  if (smallest_distance == 700000 || closest == -1)
  {
   return;
  }

  seeker[w][s].target = closest;

}








void destroy_seeker(int w, int s)
{
  seeker[w][s].active = 0;
}




