#include "empire.h"
#include "battleshot.h"

extern int rounded_health(float h);
extern int attackable_unit_here(_unit *attacker, int tx, int ty);
extern int player_color(int c);

void do_damage(_unit *a, _unit *d);
void apply_player_bonuses(float& basic, _unit *a, _unit *d);
bool rotate_units(_unit *a, _unit *d, int rotationspeed);
bool battle_attack(_unit *a, _unit *d);
int do_battle(_unit *a, _unit *d);
int basic_damage(int a, int d);
int defbonus(int tx, int ty);
int attack_sound(int utype, int dtype);
float total_damage(_unit *a, _unit *d);

int do_battle(_unit *a, _unit *d)
{
	const short SKIPPING_ANIMATION = -1;
	const short UNITS_ROTATING     = 0;
	const short ATTACKER_FIRING    = 1;
	const short DEFENDER_FIRING    = 2;
	const short FINISHED           = 3;
  static bool first = true;
	static bool attacked_twice;
	static short battlestatus;
  int temp, percentile;
	
  if (first)
  {
		if (global.battle_animation == 1)
		{
      battlestatus = UNITS_ROTATING;
		}
		else
		{
			battlestatus = SKIPPING_ANIMATION;
		}
    attacked_twice = false;
    first = false;
  }
	
	switch(battlestatus)
	{
		case SKIPPING_ANIMATION:
			do_damage(a, d);
      if ((d->health >= 0.01) && (d->attacktype != RANGED) && (a->attacktype != RANGED))
      {  //if neither unit is a ranged-attack unit
        if (attackable_unit_here(d, a->tilex, a->tiley) != -1)
        {  //if the defender can counterattack, do so (but not if it's allied)
          do_damage(d, a);
        }
      }
      if ((player[a->color].powered == 1) && (!attacked_twice))
      {  //if the attacking player is powered up
        if (currentcharacter[a->color]->power[PWR_DOUBLESHOT] > 0)
        {  //if the power uses doubleshot
          if (a->attacktype == RANGED)
          {  //if it's a ranged unit that just attacked
            if (d->exists == 1)
            {  //if the defender is still alive
							percentile = 25 * (currentcharacter[a->color]->power[PWR_DOUBLESHOT]);
							attacked_twice = true;
              temp = rand()%100;
              if (temp < percentile)
              {
                do_damage(a, d);  //attacker fires twice
              }
            }
          }
        }
      }
			battlestatus = FINISHED;
			break;
		case UNITS_ROTATING:
			if (rotate_units(a, d, 5))  //rotate_units returns true when it's finished
			{  //rotate fast, then rotate again with more accuracy
				if (rotate_units(a, d, 1))
			  {
  				battlestatus = ATTACKER_FIRING;
  			}
			}
			break;
	  case ATTACKER_FIRING:
			if (battle_attack(a, d))
			{
				do_damage(a, d);
				battlestatus = FINISHED;
				if ((d->health > 0) && (d->attacktype != RANGED) && (a->attacktype != RANGED))
        {  //if neither unit is a ranged-attack unit
          if (attackable_unit_here(d, a->tilex, a->tiley) != -1)
          {  //if the defender can counterattack, do so (but not if it's allied)
            battlestatus = DEFENDER_FIRING;
          }
        }
				if ((player[a->color].powered == 1) && (!attacked_twice))
        {  //if the attacking player is powered up		
	        if (currentcharacter[a->color]->power[PWR_DOUBLESHOT] > 0)
  	      {  //if the power uses doubleshot
    	      if (a->attacktype == RANGED)
      	    {  //if it's a ranged unit that just attacked
        	    if (d->exists == 1)
          	  {  //if the defender is still alive
								percentile = 25 * (currentcharacter[a->color]->power[PWR_DOUBLESHOT]);
								attacked_twice = true;
	              temp = rand()%100;
  	            if (temp < percentile)
    	          {
	   							attacked_twice = true;
									battlestatus = ATTACKER_FIRING;
        	      }
	            }
  	        }
    	    }
        }
			}
			break;
		case DEFENDER_FIRING:
			if (battle_attack(d, a))
			{
				do_damage(d, a);
				battlestatus = FINISHED;
			}
			break;
		case FINISHED:
		default:
			a->ready = 0;  // For battle animations, the unit needs to be marked "unready" afterwards
			first = true;
		  return 0;
	}
  return 1;
}

bool rotate_units(_unit *a, _unit *d, int rotationspeed)
{  //if it's a direct attack, rotate the defender to return fire
	static bool first = true;
	static int arotate, drotate;
	bool rotate_defender = (a->attacktype == DIRECT);
	bool adone = false;
	bool ddone = false;
	
	if (first)
	{
		arotate = int((256 * atan2(d->tiley - a->tiley, d->tilex - a->tilex)) / (2 * M_PI));
		drotate = int((256 * atan2(a->tiley - d->tiley, a->tilex - d->tilex)) / (2 * M_PI));
  	while (arotate > 255) arotate -= 256;  //make sure rotation is between 0 and 255
  	while (arotate < 0) arotate += 256;
		while (drotate > 255) drotate -= 256;  //make sure rotation is between 0 and 255
  	while (drotate < 0) drotate += 256;
		first = false;
	}
	
	if (abs(arotate - a->rotation) > rotationspeed)
	{
		if (a->rotation < arotate)
		{
			if (abs(a->rotation - arotate) > 128)
			{
				a->rotation -= rotationspeed;
			}
			else
			{
				a->rotation += rotationspeed;
			}
		}
	  else
		{
			if (abs(a->rotation - arotate) > 128)
			{
				a->rotation += rotationspeed;
			}
			else
			{
	  		a->rotation -= rotationspeed;
			}
		}
	}
	else
	{
		adone = true;
	}
	
	if (rotate_defender)
	{
		if (abs(drotate - d->rotation) > rotationspeed)
		{
  		if (d->rotation < drotate)
		  {
  			if (abs(d->rotation - drotate) > 128)
			  {
  				d->rotation -= rotationspeed;
			  }
			  else
			  {
  				d->rotation += rotationspeed;
			  }
		  }
	    else
		  {
  			if (abs(d->rotation - drotate) > 128)
			  {
  				d->rotation += rotationspeed;
			  }
			  else
			  {
  	  		d->rotation -= rotationspeed;
			  }
		  }
		}
		else
		{
			ddone = true;
		}
	}
	else
	{
		ddone = true;
	}
	
	if ((adone) && (ddone))
	{
		first = true;
		return true;
	}
	return false;
}

bool battle_attack(_unit *a, _unit *d)
{  //returns true when finished
	static bool first = true;
	static bool createparticles = true;
	static bool createdparticles = false;
	static int y, offset_y, btype, l;
	vector<_loc> points;
	int i, j;
	
	if (first)
	{
		l = -1;
		y = 0;
		offset_y = 0;
		createparticles = true;
		createdparticles = false;
		
    switch (a->type)
		{			
			case ANTI_AIR:
			case INFANTRY:
			case RECON:
				btype = B_BULLET;
			  break;
			case FIGHTER:
			case MISSILES:
				btype = B_ROCKET;
			  break;
			case ROCKETS:
				l = 20;
				btype = B_BALLISTIC;
				break;
      case ARTILLERY:
			case B_SHIP:
				btype = B_BALLISTIC;
				break;
			case B_COPTER:
				if ((d->type == INFANTRY) || (d->type == MECH) || (d->basetype == AIR))
				{
					btype = B_BULLET;
				}
				else
				{
					btype = B_ROCKET;
				}
				break;
			case BOMBER:
				btype = B_BOMB;
			  createparticles = false;
			  break;
			case CRUISER:
				if (d->basetype == AIR)
				{
					btype = B_BULLET;
				}
				else
				{
					btype = B_ROCKET;
				}
				break;
			case MD_TANK:
			case TANK:
				btype = B_CANNON;
			  break;
			case MECH:
				if ((d->basetype == LAND) && (d->type != INFANTRY) && (d->type != MECH))
				{
					btype = B_ROCKET;
				}
				else
				{
					btype = B_BULLET;
				}
				break;
			case SUB:
				btype = B_TORPEDO;
			  break;
		}
		
		first = false;
	}

	if ((createparticles) && (!createdparticles))
	{
    points = bulletpoints(a, d);
	  i = 0;
	  while (i < rounded_health(a->health))
	  {
 			j = rand()%points.size();
		  create_battleshot(btype, MAP_TILE_SIZE * a->tilex + points[j].x, 
												MAP_TILE_SIZE * a->tiley + points[j].y + offset_y, 
											  MAP_TILE_SIZE * d->tilex + MAP_TILE_SIZE / 2, 
												MAP_TILE_SIZE * d->tiley + MAP_TILE_SIZE / 2,
											  i * 3, attack_sound(a->type, d->type), l);
		  i++;
	  }
		createdparticles = true;
	}
	
	if (!createparticles)
	{
		switch (a->type)
		{
			case BOMBER:
				a->reposition(0, -1);
			  y++;
			  if (y >= MAP_TILE_SIZE / 2)
				{
					createparticles = true;
				}
				offset_y--;
			  break;
		}
	}
	else if (do_battleshots())
	{
		if (offset_y != 0)
		{
			a->reposition(0, -offset_y);
		}
		first = true;
	  return true;
	}
	return false;
}

int basic_damage(int a, int d)
{                 //A-a, APC, Art, B-c, B-s, Bo, Cr, Fi, Inf, Lan, Md, Mch, Mis, Rcn, Roc, Sub, T-c, Tank
  int damage[324] = {45,  50,  50, 120,  0,  75,  0,  65, 105,  0, 10, 105,  55,  60,  55,  0, 120,  25,
      /*APC*/         0,   0,   0,   0,  0,   0,  0,   0,   0,  0,  0,   0,   0,   0,   0,  0,   0,   0,
      /*Artillery*/  75,  70,  75,   0, 40,   0, 65,   0,  90, 55, 45,  85,  80,  80,  80, 60,   0,  70,
      /*B-Copter*/   25,  60,  65,  65, 25,   0, 55,   0,  75, 25, 25,  75,  65,  55,  65, 25,  95,  55,
      /*B-Ship*/     85,  80,  80,   0, 50,   0, 95,   0,  95, 95, 55,  90,  90,  90,  85, 95,   0,  80,
      /*Bomber*/     95, 105, 105,   0, 75,   0, 85,   0, 110, 95, 95, 110, 105, 105, 105, 95,   0, 105,
      /*Cruiser*/     0,   0,   0, 115,  0,  65,  0,  55,   0,  0,  0,   0,   0,   0,   0, 90, 115,   0,
      /*Fighter*/     0,   0,   0, 100,  0, 100,  0,  55,   0,  0,  0,   0,   0,   0,   0,  0, 100,   0,
      /*Infantry*/    5,  14,  15,   7,  0,   0,  0,   0,  55,  0,  1,  45,  25,  12,  25,  0,  30,   5,
      /*Lander*/      0,   0,   0,   0,  0,   0,  0,   0,   0,  0,  0,   0,   0,   0,   0,  0,   0,   0,
      /*Md-Tank*/   105, 105, 105,  12, 10,   0, 55,   0, 105, 35, 55,  95, 105, 105, 105, 10,  45,  85,
      /*Mech*/       65,  75,  70,   9,  0,   0,  0,   0,  65,  0, 15,  55,  85,  85,  85,  0,  35,  55,
      /*Missiles*/    0,   0,   0, 120,  0, 100,  0, 100,   0,  0,  0,   0,   0,   0,   0,  0, 120,   0,
      /*Recon*/       4,  45,  45,  10,  0,   0,  0,   0,  70,  0,  1,  65,  28,  35,  55,  0,  35,   6,
      /*Rockets*/    85,  80,  80,   0, 55,   0, 85,   0,  95, 60, 55,  90,  90,  90,  85, 85,   0,  80,
      /*Sub*/         0,   0,   0,   0, 55,   0, 25,   0,   0, 95,  0,   0,   0,   0,   0, 55,   0,   0,
      /*T-Copter*/    0,   0,   0,   0,  0,   0,  0,   0,   0,  0,  0,   0,   0,   0,   0,  0,   0,   0,
      /*Tank*/       65,  75,  70,  10,  1,   0,  5,   0,  75, 10, 15,  70,  85,  85,  85,  1,  40,  55};
  return damage[(18 * a) + d];
}

int defbonus(int tx, int ty)
{
  int s = worldmap.tile[tx][ty].get_set();
  int n = worldmap.tile[tx][ty].get_number();
  switch(s)
  {
    case 0:  //Land
      switch(n)
      {
        case 2:
          return 2;
          break;
        case 3:
          return 1;
          break;
        case 4:
          return 4;
          break;
        default:
          return 0;
          break;
      }
      break;
    case 1:  //River
      return 0;
      break;
    case 2:  //Sea
      if (n == 1)  //Reef
      {
        return 1;
      }
      else
      {
        return 0;
      }
      break;
    case 3:  //Shoal
      return 0;
      break;
    case 4:  //Building
      if (worldmap.tile[tx][ty].is_HQ())
      {
        return 4;
      }
      return 3;
      break;
  }
  return 0;
}

float total_damage(_unit *a, _unit *d)
{
  float basic = float(basic_damage(a->type, d->type)) * a->health / 10;
  int def = defbonus(d->tilex, d->tiley);
  if (d->basetype == AIR)
  {  //air units have no terrain defense bonus
    def = 0;
  }
  apply_player_bonuses(basic, a, d);
  return basic - ((basic * float(def)) / 10);
}

void do_damage(_unit *a, _unit *d)
{
  float total = total_damage(a, d);
  a->ammo--;

  if (player[d->color].powered == 0)
  {  //power does not increase on the turn the power is used
    player[d->color].power += (int(total) * d->price) / 10000;
  }
  if (player[d->color].power > currentcharacter[d->color]->powerneeded)
  {
    player[d->color].power = currentcharacter[d->color]->powerneeded;
  }

  if (player[a->color].powered == 0)
  {  //power does not increase on the turn the power is used
    player[a->color].power += (int(total) * d->price) / 20000;
  }
  if (player[a->color].power > currentcharacter[a->color]->powerneeded)
  {
    player[a->color].power = currentcharacter[a->color]->powerneeded;
  }

  d->health -= total / 10;
  if (d->health < 0.01)
  {
    d->kill();
  }
}

int attack_sound(int utype, int dtype)
{
  switch(utype)
  {
    case ANTI_AIR:
      return S_AAFIRE;
      break;
    case ARTILLERY:
      return S_MDTANKFIRE;
      break;
    case B_COPTER:
			if ((dtype == INFANTRY) || (dtype == MECH) || (dtype == B_COPTER) || (dtype == T_COPTER))
			{
				return S_AUTOGUN;
			}
			else
			{
				return S_MISSILE;
			}
      break;
    case B_SHIP:
      return S_MDTANKFIRE;
      break;
    case BOMBER:
      return S_BOMBS;
      break;
    case CRUISER:
      if ((dtype == FIGHTER) || (dtype == BOMBER) || (dtype == T_COPTER) || (dtype == B_COPTER))
      {
        return S_AUTOGUN;
      }
      else
      {
        return S_MDTANKFIRE;
      }
      break;
    case FIGHTER:
      return S_MISSILE;
      break;
    case INFANTRY:
      return S_AUTOGUN;
      break;
    case MD_TANK:
      return S_MDTANKFIRE;
      break;
    case MECH:
      if ((dtype == INFANTRY) || (dtype == MECH) || (dtype == B_COPTER) || (dtype == T_COPTER))
      {
        return S_AUTOGUN;
      }
      else
      {
        return S_MISSILE;
      }
      break;
    case MISSILES:
      return S_MISSILE;
      break;
    case RECON:
      return S_AUTOGUN;
      break;
    case ROCKETS:
      return S_MISSILE;
      break;
    case SUB:
      return S_TORPEDO;
      break;
    case TANK:
      return S_TANKFIRE;
      break;
  }
	return S_AUTOGUN;
}

void apply_player_bonuses(float& basic, _unit *a, _unit *d)
{
	bool nopower;
	int i = 0;
	
  switch(currentcharacter[a->color]->strength)
  {
    case 0:
      if (a->attacktype == DIRECT)
      {
        basic += 5;
      }
      break;
    case 1:
      if (a->attacktype == RANGED)
      {
        basic += 5;
      }
      break;
    case 2:
      if (a->movetype == LAND)
      {
        basic += 5;
      }
      break;
    case 3:
      if (a->movetype == AIR)
      {
        basic += 5;
      }
      break;
    case 4:
      if (a->movetype == SEA)
      {
        basic += 5;
      }
      break;
  }
  switch(currentcharacter[a->color]->weakness)
  {
    case 0:
      if (a->attacktype == DIRECT)
      {
        basic -= 5;
      }
      break;
    case 1:
      if (a->attacktype == RANGED)
      {
        basic -= 5;
      }
      break;
    case 2:
      if (a->movetype == LAND)
      {
        basic -= 5;
      }
      break;
    case 3:
      if (a->movetype == AIR)
      {
        basic -= 5;
      }
      break;
    case 4:
      if (a->movetype == SEA)
      {
        basic -= 5;
      }
      break;
  }
  switch(currentcharacter[d->color]->strength)
  {
    case 2:
      if (d->movetype == LAND)
      {
        basic -= 5;
      }
      break;
    case 3:
      if (d->movetype == AIR)
      {
        basic -= 5;
      }
      break;
    case 4:
      if (d->movetype == SEA)
      {
        basic -= 5;
      }
      break;
  }
  switch(currentcharacter[d->color]->weakness)
  {
    case 2:
      if (d->movetype == LAND)
      {
        basic += 5;
      }
      break;
    case 3:
      if (d->movetype == AIR)
      {
        basic += 5;
      }
      break;
    case 4:
      if (d->movetype == SEA)
      {
        basic += 5;
      }
      break;
  }
	
	nopower = true;
  if (player[a->color].powered == 1)
  {
    i = 0;
		while (i < 6)
		{
			switch(i)
    	{  //if the attacker has...
      	case PWR_DIRECT:  //more powerful direct attacks
					nopower = false;
					if (a->attacktype == DIRECT)
        	{
	          basic *= int(1.0 + (0.1 * float(currentcharacter[a->color]->power[i])));
        	}
        	break;
      	case PWR_RANGED:  //more powerful ranged attacks
					nopower = false;
	        if (a->attacktype == RANGED)
        	{
	          basic *= int(1.0 + (0.1 * float(currentcharacter[a->color]->power[i])));
        	}
        	break;
			}
			i++;
    }
  }
	if (nopower)
	{
		basic += 2;
	}
	
  if (player[d->color].powered == 1)
  {
    basic -= 2;  //powered units take less damage
  }
  if (basic < 0)
  {
    basic = 0;
  }  
}
