#include "empire.h"

_action action[3];

#define AI_UNIT_BEGIN        0
#define AI_UNIT_SCROLL       1
#define AI_UNIT_MAKE_PATHMAP 2
#define AI_UNIT_THINK        3
#define AI_UNIT_PAUSE        4
#define AI_UNIT_ACT          5

bool ai_unit_animating;
_action ai_unit_animation;
int in_battle, atk, def;
vector<_unit*> lander;
vector<_unit*> need_transport;
vector<_loc> base;
vector<_aitarget> targets;
_loc capital;
vector<_aitarget> list_targets(_unit *u);

int do_ai(int plyr)
{
  return do_ai(plyr, false);
}

int do_ai(int plyr, bool reset_everything)
{
  static bool first = true;
  static int i = 0;
  static _actionset a;
  static int draw_stats, last_statpos;
  static int initial_delay, delay, aistatus, anum;
  int t1, t2, t3, t4;
  int powerflash = 0;
  _tile *thetile;
  _loc temploc;
	
  if (reset_everything)
  {  //if the user returned to the main menu in the middle of an AI turn
    first = 1;
    return 1;
  }
  
  if (first)
  {
    lander.clear();
    need_transport.clear();
    player[plyr].sort_units();
    i = 0;
    while (i < 50)
    {
      if (player[plyr].unit[i].exists == 1)
      {
        if (player[plyr].unit[i].type == LANDER)
        {  //this creates a vector of pointers to landers, handy later on
          lander.push_back(&player[plyr].unit[i]);
        }
      }
      i++;
    }
    t2 = 0;
    while (t2 < worldmap.h)
    {
      t1 = 0;
      while (t1 < worldmap.l)
      {
        thetile = &worldmap.tile[t1][t2];
        if (thetile->owned_by(plyr))
        {
          if (thetile->is_unit_producing())
          {  //if it belongs to the player and produces units
            temploc.x = t1;
            temploc.y = t2;
            base.push_back(temploc);  //create a vector of base locations
          }
          else if (thetile->is_HQ())
          {  //mark the location of the player's HQ
            capital.x = t1;
            capital.y = t2;
          }
        }
        t1++;
      }
      t2++;
    }
    i = 0;
    first = false;
    in_battle = 0;
    atk = -1;
    def = -1;
    draw_stats = 0;
    last_statpos = -1;
		initial_delay = 0;
    delay = 0;
    if (player[plyr].power == currentcharacter[plyr]->powerneeded)
    {
      player[plyr].powered = 1;
      player[plyr].power = 0;
      do_power();
      powerflash = 1;  //start the power animation
    }
		aistatus = AI_UNIT_BEGIN;
    counter = 0;
  }
  
  if (initial_delay < 15)
  {  //have a little delay before the units start doing stuff
    initial_delay++;
  }
  else if (in_battle == 0)
  {
    if (check_explodes())
    {  //if check_explodes found an explosion to do
      return 30;  //get out of the AI function early and indicate an explosion
    }
    
    draw_playerstats(plyr + (draw_stats * 10));  //see draw_playerstats for details on this
    if (i < 50)
    {
      _unit *z = &player[plyr].unit[i];
      if ((z->exists == 1) || (ai_unit_animating))
      {  //the (ai_unit_animating) is because the unit doesn't exist for part of the teleport animation
        if (z->ready == 1)
        {
          switch (aistatus)
					{
						case AI_UNIT_BEGIN:
							if (delay < 15)
          		{  //have a little delay between units as well
            		delay++;
          		}
							else
							{
								aistatus = AI_UNIT_SCROLL;
							}
							break;
						case AI_UNIT_SCROLL:
	            if (scroll_to_location(z->tilex, z->tiley) == 1)
  	          {  //smooth scroll to the unit, which returns 1 when finished
    	          aistatus = AI_UNIT_MAKE_PATHMAP;
      	        return 20;  //returning a 2 in the tens column prevents the player from bringing up a menu
        	    }
							break;
						case AI_UNIT_MAKE_PATHMAP:
	            worldmap.create_pathmap(plyr, z->movetype, z->tilex, z->tiley);
            	show_movable_tiles(z);
            	counter = 0;  //reset the counter, since creating the pathmap might take a while
            	aistatus = AI_UNIT_THINK;
							break;
						case AI_UNIT_THINK:
	            show_movable_tiles(z);
							t1 = (z->tilex - worldmap.scroll_x) * MAP_TILE_SIZE;
           		t2 = (z->tiley - worldmap.scroll_y) * MAP_TILE_SIZE;
           		switch(z->color)
           		{
	              case 0:
               		t3 = ORANGE;
               		break;
             		case 1:
	                t3 = BLUE;
               		break;
             		case 2:
	                t3 = GREEN;
               		break;
             		case 3:
	                t3 = YELLOW;
               		break;
					  		default:
									t3 = BLACK;
		    					break;
           		}  //draw a rectangle around the unit, colored the same as the unit's team
           		buffer_rect(t1 + 1, t2 + 1, t1 + MAP_TILE_SIZE - 1, t2 + MAP_TILE_SIZE - 1, t3);
            	targets.resize(0);
            	a = ponder_action(z);
            	delay = 0;
							aistatus = AI_UNIT_PAUSE;
            	counter = 0;  //this may have taken a while, but nothing happened on
          		break;        //screen, so just set the counter like it took no time
						case AI_UNIT_PAUSE:
          		if (delay < 25)  //the unit's range is displayed for 25 logic loops (.5 seconds)
          		{  //in other words, it shows the range while thinking, then pauses before acting
            		show_movable_tiles(z);
								z->draw(false);
								t1 = (z->tilex - worldmap.scroll_x) * MAP_TILE_SIZE;
	           		t2 = (z->tiley - worldmap.scroll_y) * MAP_TILE_SIZE;
           			switch(z->color)
           			{
	              	case 0:
               			t3 = ORANGE;
	               		break;
             			case 1:
	                	t3 = BLUE;
	               		break;
             			case 2:
	                	t3 = GREEN;
	               		break;
             			case 3:
	                	t3 = YELLOW;
	               		break;
					  			default:
										t3 = BLACK;
		    						break;
           			}  //draw a rectangle around the unit, colored the same as the unit's team
           			buffer_rect(t1 + 1, t2 + 1, t1 + MAP_TILE_SIZE - 1, t2 + MAP_TILE_SIZE - 1, t3);
            		delay++;
							}
            	if (delay == 25)
            	{
								aistatus = AI_UNIT_ACT;
								anum = 0;
	              if (z->tilex - worldmap.scroll_x < 4)
              	{
	                draw_stats = 2;
              	}
              	else
              	{
	                draw_stats = 1;
              	}
            	}
							break;
						case AI_UNIT_ACT:
	            if ((!z->is_moving()) && (!ai_unit_animating))
            	{  //if the unit has is finished with an action or starting the first
	              switch(anum)
              	{
	                case 0:
                	case 1:
                	case 2:
	                  act(&player[plyr].unit[i], a.action[anum]);
                  	anum++;
                  	if (z->exists == 0)
                  	{  //if a battle or being loaded into a transport causes it to disappear
	                    z->ready = 0;
                    	aistatus = AI_UNIT_BEGIN;
                    	delay = 0;
                  	}
                  	break;
                	case 3:  //if the final action has just been done, this unit is finished
	                  z->ready = 0;
                  	aistatus = AI_UNIT_BEGIN;
                  	delay = 0;
                  	break;
              	}
            	}
            	else if (ai_unit_animating)
							{
								switch(ai_unit_animation.type)
								{
									case action_teleport:
										ai_teleport(&player[plyr].unit[i], ai_unit_animation.loc);
										break;
								}
							}
							else
            	{
	              z->do_movement();
              	z->draw();
            	}
							break;
					}
        }
        else  //if the unit is not ready, go to the next one
        {
          i++;
//          unit_delay = 0;
        }
      }
      else  //if the unit does not exist, go to the next one
      {
        i++;
//        unit_delay = 0;
      }
    }
    if (i == 50)  //if it has just finished with the 50th unit
    {
      if (delay == 0)
      {
        make_new_units(plyr);
        counter = 0;  //this also tends to take a while, but has no on-screen effect
        delay++;
      }
      else if (delay < 35)
      {  //pause for a little over half a second after the units are made
        delay++;
      }
      else
      {
        first = true;
        delay = 0;
        set_mouse_range(0, 0, 639, 479);
        return 1;  //indicate that it is done
      }
    }
  }
  else  //if there is a battle animation going on
  {
    t1 = atk / 100;
    t2 = atk % 100;
    t3 = def / 100;
    t4 = def % 100;
    in_battle = do_battle(&player[t1].unit[t2], &player[t3].unit[t4]);
    return 20;
  }
  if (powerflash != 1)
  {
    return 0;  //indicate that it is not finished
  }
  else
  {
    return 10;
  }
}

_actionset ponder_action(_unit *u)
{
  _actionset aset;
  action[0].type = action_standby;
  action[0].loc.x = -1;
  action[0].loc.y = -1;
  action[1].type = action_standby;
  action[1].loc.x = -1;
  action[1].loc.y = -1;
  action[2].type = action_standby;  //third action only used for unloading landers
  action[2].loc.x = -1;
  action[2].loc.y = -1;
  targets = list_targets(u);
	
	check_capturing(u);         //these two functions just check what the unit can do in
  check_attackable_units(u);  //one turn, while the following do closest moves to stuff
	
	if (u->gas == 0)
	{
	  aset.action[0] = action[0];
  	aset.action[1] = action[1];
  	aset.action[2] = action[2];
  	return aset;
	}  //all the stuff after this requires the unit to be able to move
	
	check_low_health(u);

  if (action[0].type == action_standby)
  {  //if it didn't capture, doesn't need to get fixed, and can't attack this turn
    if ((u->type == INFANTRY) || (u->type == MECH))
    {  //if it can capture things, head for the closest capturable building
      check_closest_capturable(u);
    }
    if ((u->type != APC) && (u->type != T_COPTER) && (u->type != LANDER))
    {
      if (action[0].type == action_standby)
      {
        check_closest_attackable(u);
      }
    }
    else if (u->type == APC)
    {
      check_APC_options(u);
    }
    else if (u->type == T_COPTER)
    {
      check_tcopter_options(u);
    }
    else
    {  //if the unit is a lander
      check_lander_options(u);
    }
  }
	check_teleports(u);  //if the unit is moving somewhere, see if it can get there faster by teleporting
											 //if the unit has nowhere to go, see if it can get somewhere by teleporting
  if ((u->type == INFANTRY) || (u->type == MECH))
  {
    check_convenient_APC(u);
    if (action[0].type == action_standby)
    {
      check_available_copter(u);
    }
  }
  if (action[0].type == action_standby)
  {  //if there are no enemies to attack or teleport tiles handy, cover an enemy base
    check_enemy_bases(u);
  }
  if ((u->basetype == LAND) && (action[0].type == action_standby))
  {  //check for a lander if the unit can't do anything and could be moved in a lander
    check_available_lander(u);
  }
  if (action[0].type == action_standby)
  {  //make sure it isn't preventing other units from being built
		check_blocking_base(u);
  }
  if ((u->type != APC) && (u->type != LANDER) && (u->type != T_COPTER))
  {  //if it isn't a transport
    if (u->basetype == LAND)
    {
      if (action[0].type == action_standby)
      {  //if it still isn't doing anything
        need_transport.push_back(u);
      }
    }
  }
  aset.action[0] = action[0];
  aset.action[1] = action[1];
  aset.action[2] = action[2];
  return aset;
}

void act(_unit *u, _action a)
{
  int i;
  int q;
	char dbg[200];
  switch(a.type)
  {
    case action_move:
      if ((u->tilex != a.loc.x) || (u->tiley != a.loc.y))
      {  //a crash safeguard: units don't move if they're already at the destination
        if ((a.loc.x != -1) && (a.loc.y != -1))
        {
          set_best_path(u, a.loc.x, a.loc.y);
          play_moving_sound(u->type);
          u->do_movement();
        }
        else
        {  //move should never be called for location (-1, -1), so log this error
					sprintf(dbg, "Error: A %s unit at (%d, %d) tried to move to point (-1, -1)", unit_name(u->type).c_str(), u->tilex, u->tiley);
          empdebug(dbg);
        }
      }
      break;
    case action_attack:
      in_battle = 1;
      def = any_unit_here(a.loc.x, a.loc.y);
      atk = (u->color * 100) + u->number;
      break;
    case action_capture:
      capture_building(u);
      break;
    case action_load:
      q = unit_here_thorough(u->color, u->tilex, u->tiley, u->number);
      player[u->color].unit[q].load_unit(u);      
      worldmap.tile[u->tilex][u->tiley].set_unit(u->color, q);
      player[u->color].unit[u->number].exists = 0;
      break;
    case action_unload1:
      u->load[0].loaded = 0;
      i = player[u->color].create_unit(u->load[0].type, a.loc.x, a.loc.y, u->color);
      player[u->color].unit[i].gas = u->load[0].gas;
      player[u->color].unit[i].ammo = u->load[0].ammo;
      player[u->color].unit[i].health = u->load[0].health;
      if (u->load[0].subload.loaded == 1)
      {
        player[u->color].unit[i].load[0].loaded = 1;
        player[u->color].unit[i].load[0].type = u->load[0].subload.type;
        player[u->color].unit[i].load[0].gas = u->load[0].subload.gas;
        player[u->color].unit[i].load[0].ammo = u->load[0].subload.ammo;
        player[u->color].unit[i].load[0].health = u->load[0].subload.health;
        u->load[0].subload.loaded = 0;
      }
      break;
    case action_unload2:
      u->load[1].loaded = 0;
      i = player[u->color].create_unit(u->load[1].type, a.loc.x, a.loc.y, u->color);
      player[u->color].unit[i].gas = u->load[1].gas;
      player[u->color].unit[i].ammo = u->load[1].ammo;
      player[u->color].unit[i].health = u->load[1].health;
      if (u->load[1].subload.loaded == 1)
      {
        player[u->color].unit[i].load[0].loaded = 1;
        player[u->color].unit[i].load[0].type = u->load[1].subload.type;
        player[u->color].unit[i].load[0].gas = u->load[1].subload.gas;
        player[u->color].unit[i].load[0].ammo = u->load[1].subload.ammo;
        player[u->color].unit[i].load[0].health = u->load[1].subload.health;
        u->load[1].subload.loaded = 0;
      }      
      break;
    case action_standby:
      break;  //do nothing on standby
    case action_supply:
      supply_units(u);
      break;
    case action_merge:
      merge_units(u, u->tilex, u->tiley);
      break;
		case action_teleport:
			ai_unit_animating = true;
			ai_unit_animation.type = action_teleport;
			ai_unit_animation.loc = a.loc;
			ai_unit_animation.destination = a.destination;
			break;
  }
}

void set_best_path(_unit *u, int tx, int ty)
{
  int i, q;
  vector<int> list;
  list.reserve(10);
  vector<_aipath> t = best_aipath(u->color, u->movetype, u->tilex, u->tiley, tx, ty);
  q = t.size();
  i = 0;
  while (i < q)
  {
    list.push_back(t[i].dir);
    i++;
  }
  u->set_moves(list);
}

int any_unit_here(int tx, int ty)
{
  if (worldmap.in_bounds(tx, ty))
  {
    return worldmap.tile[tx][ty].unit_here();
  }
  return -1;
}

int next_to_enemy(int plyr, int tx, int ty)
{
  int z;
  z = any_unit_here(tx + 1, ty);
  if (z != -1)
  {
    if (z / 100 != plyr)
    {
      return 1;
    }
  }
  z = any_unit_here(tx - 1, ty);
  if (z != -1)
  {
    if (z / 100 != plyr)
    {
      return 1;
    }
  }
  z = any_unit_here(tx, ty + 1);
  if (z != -1)
  {
    if (z / 100 != plyr)
    {
      return 1;
    }
  }
  z = any_unit_here(tx, ty - 1);
  if (z != -1)
  {
    if (z / 100 != plyr)
    {
      return 1;
    }
  }
  return 0;   
}

int needy(int unit_id)
{
  int p = unit_id / 100;
  int u = unit_id % 100;
  if ((player[p].unit[u].gas < 25) || ((player[p].unit[u].ammo < 4) && (player[p].unit[u].attacktype != -1)))
  {
    return 1;
  }
  else
  {
    return 0;
  }
}

int next_to_needy_friendly(int plyr, int tx, int ty)
{
  int z;
  z = needy(any_unit_here(tx + 1, ty));
  if (z == 1)
  {
    return 1;
  }
  z = needy(any_unit_here(tx - 1, ty));
  if (z == 1)
  {
    return 1;
  }
  z = needy(any_unit_here(tx, ty + 1));
  if (z == 1)
  {
    return 1;
  }
  z = needy(any_unit_here(tx, ty - 1));
  if (z == 1)
  {
    return 1;
  }
  return 0;
}

int intmax(int a, int b)
{
  if (a > b)
  {
    return a;
  }
  return b;
}

int intmin(int a, int b)
{
	if (a > b)
	{
		return b;
	}
	return a;
}

bool enemy_threat(int plyr)
{  //checks to see whether enemy units are near a base or the HQ
  int i = 0;
  int z = base.size();
  while (i < z)
  {
    if (enemy_within_radius(plyr, base[i].x, base[i].y, 10) == true)
    {
      return true;
    }
    i++;
  }
  if (enemy_within_radius(plyr, capital.x, capital.y, 10) == true)
  {
    return true;
  }
  return false;
}

bool enemy_within_radius(int plyr, int center_x, int center_y, int r)
{
 int x, y, t;
  y = center_y - r;
  while (y <= center_y + r)
  {
    x = center_x - r;
    while (x <= center_x + r)
    {
      if ((worldmap.in_bounds(x, y)) && (tile_distance(x, y, center_x, center_y) <= r))
      {
        t = any_unit_here(x, y);
        if (t != -1)
        {
          if (player[t / 100].team != player[plyr].team)
          {  //doesn't check whether the enemy unit can get to the base
            return true;  //only if it's within R tiles
          }  //yeah, I know it needs work
        }
      }
      x++;
    }
    y++;
  }
  return false;
}

void ai_teleport(_unit *u, _loc dest)
{
  static bool first = true;
  static int time;
  int t1, t2;
  
  if (first)
  {
    time = 0;
    first = false;
  }
  
  if (time == 0)
  {
    if (scroll_to_location(u->tilex, u->tiley) == 0)
    {
      time--;
    }
  }
  else if (time == 1)
  {
    t1 = u->tilex * MAP_TILE_SIZE;
    t2 = u->tiley * MAP_TILE_SIZE;
    create_particle(t1 + MAP_TILE_SIZE / 2, t2 + MAP_TILE_SIZE / 2, P_SHOCKWAVE);
    create_particle(t1, t2, P_TELEPORT);
    play_sound(S_TELEPORT);
  }
  else if (time == 9)
  {
    u->exists = 0;
  }
  else if (time == 25)
  {
    if (scroll_to_location(dest.x, dest.y) == 0)
    {
      time--;
    }
  }
  else if (time == 26)
  {
    u->set_tiles(dest.x, dest.y);
    create_particle(dest.x * MAP_TILE_SIZE + MAP_TILE_SIZE / 2, dest.y * MAP_TILE_SIZE + MAP_TILE_SIZE / 2, P_SHOCKWAVE);
    create_particle(dest.x * MAP_TILE_SIZE, dest.y * MAP_TILE_SIZE, P_TELEPORT);
    play_sound(S_TELEPORT);
  }
  else if (time == 34)
  {
    u->exists = 1;
  }
  else if (time > 50)
  {
    first = true;
    ai_unit_animating = false;
  }
  time++;
}
