#include "empire.h"

extern void draw_tile(int set, int pic, int x, int y);
extern void init_explode(int x, int y);
extern int get_set(int mapdat);
extern int get_tilenum(int mapdat);
extern int moves_needed(int mtype, int tx, int ty);
extern bool unit_unloadable_here(int ox, int oy, int tx, int ty, int type);
extern int rounded_health(float h);
extern int any_unit_here(int tx, int ty);

int unit_price[18] = {8000, 5000, 6000, 9000, 28000, 22000, 18000, 20000, 1000,
                     12000, 16000, 3000, 12000, 4000, 15000, 20000, 5000, 7000};

int angle(int dir);
int angle_direction(int rot);

_unit::_unit()
{
  type = -1;
  color = -1;
  number = -1;
  ready = 0;
  price = -1;
  health = -1;
  exists = 0;
  basetype = -1;
  tilex = -1;
  tiley = -1;
  move = -1;
  movetype = -1;
  gas = -1;
  ammo = -1;
  submerged = 0;
	powertint = rand()%118 + 10;
	tintspeed = 2;
  attacktype = -1;
  capturing = -1;
  missioncritical = 0;
}

void _unit::create(int utype, int tx, int ty, int tcolor, int tnum)
{
  unitprops tmpr = get_properties(utype);
  move = tmpr.move;
  movetype = tmpr.movetype;
  gas = tmpr.gas;
  ammo = tmpr.ammo;
  price = tmpr.price;
  attacktype = tmpr.attacktype;
  basetype = tmpr.basetype;
  capturing = -1;
  health = 10;
  x = tx * MAP_TILE_SIZE;
  y = ty * MAP_TILE_SIZE;
	rotation = 0;
  destx = 0;
  desty = 0;
  tilex = tx;
  tiley = ty;
  missioncritical = 0;
  worldmap.tile[tilex][tiley].set_unit(tcolor, tnum);
  type = utype;
  number = tnum;
  exists = 1;
  color = tcolor;
  direction = RIGHT;
  frame = 0;
  ready = 0;
  moving = 0;
  submerged = 0;
  otherunit = -1;
  load[0].loaded = 0;
  load[0].type = -1;
  load[0].ammo = -1;
  load[0].gas = -1;
  load[0].subload.loaded = 0;
  load[0].health = -1;
  load[1].loaded = 0;
  load[1].type = -1;
  load[1].ammo = -1;
  load[1].gas = -1;
  load[1].health = -1;
  load[1].subload.loaded = 0;
  if ((type == APC) || (type == T_COPTER))
  {
    maxload = 1;
  }
  else if (type == LANDER)
  {
    maxload = 2;
  }
  else
  {
    maxload = 0;
  }
  mlist.resize(0);
}

void _unit::do_move()
{
  switch(direction)
  {
    case UP:
      y -= 4;
      break;
    case DOWN:
      y += 4;
      break;
    case LEFT:
      x -= 4;
      break;
    case RIGHT:
      x += 4;
      break;
  }
  tilex = x / MAP_TILE_SIZE;
  tiley = y / MAP_TILE_SIZE;
}

void _unit::do_movement()
{
  if (moving == 1)
  {
    rotation = angle(direction);  //if it's moving, reset its rotation
		if (otherunit != -1)
    {  //this keeps any units passed over from being erased
      worldmap.tile[tilex][tiley].set_unit(otherunit / 100, otherunit % 100);
      worldmap.draw_tiles(tilex - worldmap.scroll_x, tiley - worldmap.scroll_y, 1, 1);
    }
    else
    {
      worldmap.tile[tilex][tiley].set_unit(-1, -1);
    }
    do_move();
    otherunit = worldmap.tile[tilex][tiley].unit_here();
    if (otherunit != -1)
    {
      player[otherunit / 100].unit[otherunit % 100].draw();
    }
    worldmap.tile[tilex][tiley].set_unit(color, number);
    if ((x == destx * MAP_TILE_SIZE) && (y == desty * MAP_TILE_SIZE))
    {  //if it arrived at this move's destination
      mlist.erase(mlist.begin());
      moving = 0;
      if (mlist.size() > 0)
      {
        direction = mlist[0];
      }
    }
    frame += .08;  //if moving, the frames change more quickly
  }
  if (moving == 0)
  {
    if (mlist.size() > 0)
    {
      switch(mlist[0])
      {
        case UP:
          destx = tilex;
          desty = tiley - 1;
          break;
        case DOWN:
          destx = tilex;
          desty = tiley + 1;
          break;
        case LEFT:
          destx = tilex - 1;
          desty = tiley;
          break;
        case RIGHT:
          destx = tilex + 1;
          desty = tiley;
          break;
      }
      gas -= moves_needed(movetype, destx, desty);
      direction = mlist[0];
      moving = 1;
    }
    if (moving == 0)
    {
			rotation = angle(direction) + rand()%10 - 5;  //rotate a little, for variety
      if (otherunit != -1)
      {  //if it stops moving when on top of another unit, it is merging
        otherunit = -1;  //which means the covered unit should be erased
      }
    }
  }
}

void _unit::draw()
{
  draw(true);
}

void _unit::draw(bool increment_frame)
{
  int z = (direction - 1) * 2;
  int lx, ly, s, c;
  char tempchar[5];
	
  lx = x - (worldmap.scroll_x * MAP_TILE_SIZE) + worldmap.offset_x;
  ly = y - (worldmap.scroll_y * MAP_TILE_SIZE) + worldmap.offset_y;
  if (moving == 1)
  {
    if (basetype == LAND)
    {
      create_particle(x + MAP_TILE_SIZE / 2, y + MAP_TILE_SIZE - 5, 1);
    }
    else if (basetype == AIR)
    {
      if ((type == FIGHTER) || (type == BOMBER))
      {  //helicopter-type units don't create jet particles when moving
        create_particle(x + MAP_TILE_SIZE / 2, y + 11, 3);
        create_particle(x + MAP_TILE_SIZE / 2, y + 11, 3);
      }
    }
    else
    {
      create_particle(x + 15 + rand()%10, y + 23 + rand()%10, 4);
      create_particle(x + 15 + rand()%10, y + 23 + rand()%10, 4);
    }
    if ((type == INFANTRY) || (type == MECH) || (type == B_COPTER) || (type == T_COPTER))
    {
      if (direction == RIGHT)
      {
        z = 8;
      }
      if (direction == LEFT)
      {
        z = 10;
      }
    }
  }
	else
	{  //if it isn't moving, the unit's direction depends on its rotation
		while (rotation > 255) rotation -= 256;  //make sure rotation is between 0 and 255
  	while (rotation < 0) rotation += 256;
		direction = angle_direction(rotation);
	}
	
  if (increment_frame == true)
  {
    frame += .04;
  }
  if (frame >= 2)
  {
    frame -= 2;
  }
  if (health < 5)
  {  //start smoking if low on health
    if ((type != INFANTRY) && (type != MECH))
    {  //except if it's an infantry or mech
      if (int(frame / 0.04) % 2 == 0)
      {
        create_particle(x + 18, y + 15, P_SMOKE);
        if (health < 3)
        {
          create_particle(x + 18, y + 15, P_SMOKE);
        }
      }
    }
  }
  if (ready == 1)
  {
		if (player[color].powered == 1)
		{
			buffer_rotate_tinted_sprite(units[color][type].bmp(z + int(frame)), lx + MAP_TILE_SIZE / 2, ly + MAP_TILE_SIZE / 2, rotation - angle(direction), Rgba(WHITE, powertint));
			powertint += tintspeed;
			if (powertint > 128) tintspeed = -2;
			if (powertint < 10) tintspeed = 2;
		}
		else
		{
			buffer_rotate_sprite(units[color][type].bmp(z + int(frame)), lx + MAP_TILE_SIZE / 2, ly + MAP_TILE_SIZE / 2, rotation - angle(direction));
		}
  }
  else
  {
		if (player[color].powered == 1)
		{
			s = getr(DGREY);
			c = 255 * (s + powertint) / (255 + powertint);
			buffer_rotate_tinted_sprite(units[color][type].bmp(z), lx + MAP_TILE_SIZE / 2, ly + MAP_TILE_SIZE / 2, rotation - angle(direction), Rgba(c, c, c, 128));
			powertint += tintspeed;
			if (powertint > 128) tintspeed = -2;
			if (powertint < 10) tintspeed = 2;
		}
		else
		{
			buffer_rotate_tinted_sprite(units[color][type].bmp(z), lx + MAP_TILE_SIZE / 2, ly + MAP_TILE_SIZE / 2, rotation - angle(direction), Rgba(DGREY, 128));
		}
  }
  if (capturing != -1)
  {  //show the capture amount left
    sprintf(tempchar, "%-1d", capturing);
    buffer_textout_ex(normal, tempchar, lx, ly + MAP_TILE_SIZE - 10, LGREY);
  }
  if (submerged == 1)
  {  //show a little 's' icon if it is submerged
    buffer_draw_sprite(glb.bmp(5), lx + 14, ly, 0.5);
  }
  if (ammo < 3)
  {  //show an ammo warning if low and not a transport unit
    if ((type != APC) && (type != T_COPTER) && (type != LANDER))
    {
      buffer_draw_sprite(glb.bmp(4), lx + 7, ly, 0.5);
    }
  }
  if (gas < 20)
  {  //show a gas warning if low
    buffer_draw_sprite(glb.bmp(3), lx, ly, 0.5);
  }
  if (rounded_health(health) < 10)
  {  //if less than maximum health, show it at the bottom-right
    sprintf(tempchar, "%-1d", rounded_health(health));
    buffer_textout_ex(normal, tempchar, lx + MAP_TILE_SIZE - 7, ly + MAP_TILE_SIZE - 10, BLACK);
  }
  if (capturing != -1)
  {  //show a little 'c' icon in the top right if capturing
    buffer_draw_sprite(glb.bmp(11), lx + MAP_TILE_SIZE - 8, ly, 0.6);
  }
}

void _unit::set_moves(vector<int> list)
{
  mlist = list;
}

unitprops _unit::get_properties(int type)
{
  unitprops u;
  u.price = unit_price[type];
  switch(type)
  {
    case ANTI_AIR:
      u.move = 6;
      u.movetype = M_TREAD;
//      u.vision = 2;
      u.gas = 60;
      u.ammo = 9;
      u.attacktype = DIRECT;
      u.basetype = LAND;
      break;
    case APC:
      u.move = 6;
      u.movetype = M_TREAD;
//      u.vision = 1;
      u.gas = 70;
      u.ammo = 0;
      u.attacktype = -1;
      u.basetype = LAND;
      break;
    case ARTILLERY:
      u.move = 5;
      u.movetype = M_TREAD;
//      u.vision = 1;
      u.gas = 50;
      u.ammo = 9;
      u.attacktype = RANGED;
      u.basetype = LAND;
      break;
    case B_COPTER:
      u.move = 6;
      u.movetype = M_AIR;
//      u.vision = 3;
      u.gas = 99;
      u.ammo = 8;  //ammo is 6 in Advance Wars, but I hated how they ran out of ammo so quickly
      u.attacktype = DIRECT;
      u.basetype = AIR;
      break;
    case B_SHIP:
      u.move = 5;
      u.movetype = M_SEA;
//      u.vision = 2;
      u.gas = 99;
      u.ammo = 9;
      u.attacktype = RANGED;
      u.basetype = SEA;
      break;
    case BOMBER:
      u.move = 7;
      u.movetype = M_AIR;
//      u.vision = 2;
      u.gas = 99;
      u.ammo = 9;
      u.attacktype = DIRECT;
      u.basetype = AIR;
      break;
    case CRUISER:
      u.move = 6;
      u.movetype = M_SEA;
//      u.vision = 3;
      u.gas = 99;
      u.ammo = 9;
      u.attacktype = DIRECT;
      u.basetype = SEA;
      break;
    case FIGHTER:
      u.move = 8;
      u.movetype = M_AIR;
//      u.vision = 2;
      u.gas = 99;
      u.ammo = 9;
      u.attacktype = DIRECT;
      u.basetype = AIR;
      break;
    case INFANTRY:
      u.move = 3;
      u.movetype = M_INFANTRY;
//      u.vision = 2;
      u.gas = 99;
      u.ammo = 99;
      u.attacktype = DIRECT;
      u.basetype = LAND;
      break;
    case LANDER:
      u.move = 6;
      u.movetype = M_LANDER;
//      u.vision = 1;
      u.gas = 99;
      u.ammo = 0;
      u.attacktype = -1;
      u.basetype = SEA;
      break;
    case MD_TANK:
      u.move = 5;
      u.movetype = M_TREAD;
//      u.vision = 1;
      u.gas = 50;
      u.ammo = 8;
      u.attacktype = DIRECT;
      u.basetype = LAND;
      break;
    case MECH:
      u.move = 2;
      u.movetype = M_MECH;
//      u.vision = 2;
      u.gas = 70;
      u.ammo = 6;  //ammo is 3 in Advance Wars, but running out of ammo so fast was annoying
      u.attacktype = DIRECT;
      u.basetype = LAND;
      break;
    case MISSILES:
      u.move = 4;
      u.movetype = M_TIRES;
//      u.vision = 5;
      u.gas = 50;
      u.ammo = 6;
      u.attacktype = RANGED;
      u.basetype = LAND;
      break;
    case RECON:
      u.move = 8;
      u.movetype = M_TIRES;
//      u.vision = 5;
      u.gas = 80;
      u.ammo = 99;
      u.attacktype = DIRECT;
      u.basetype = LAND;
      break;
    case ROCKETS:
      u.move = 5;
      u.movetype = M_TIRES;
//      u.vision = 1;
      u.gas = 50;
      u.ammo = 6;
      u.attacktype = RANGED;
      u.basetype = LAND;
      break;
    case SUB:
      u.move = 5;
      u.movetype = M_SEA;
//      u.vision = 5;
      u.gas = 60;
      u.ammo = 6;
      u.attacktype = DIRECT;
      u.basetype = SEA;
      break;
    case T_COPTER:
      u.move = 6;
      u.movetype = M_AIR;
//      u.vision = 2;
      u.gas = 99;
      u.ammo = 0;
      u.attacktype = -1;
      u.basetype = AIR;
      break;
    case TANK:
      u.move = 6;
      u.movetype = M_TREAD;
//      u.vision = 3;
      u.gas = 70;
      u.ammo = 9;
      u.attacktype = DIRECT;
      u.basetype = LAND;
      break;
  }
  return u;
}

bool _unit::is_moving()
{
  if (moving == 0)
  {
    return false;
  }
  return true;
}

void _unit::supply()
{
  unitprops z = get_properties(type);
  gas = z.gas;
  ammo = z.ammo;
}

int _unit::load_unit(_unit *u)
{
  int i = 0;
  int loadok = 0;
  loadok = can_load_unit(u);
  if (load[i].loaded == 1)
  {
    i++;
  }
  if (loadok == 1)
  {
    load[i].loaded = 1;
    load[i].type = u->type;
    load[i].gas = u->gas;
    load[i].ammo = u->ammo;
    load[i].health = u->health;
    if (u->load[0].loaded == 1)
    {  //if this is a lander and the loaded unit is an APC with a passenger
      load[i].subload.loaded = 1;
      load[i].subload.type = u->load[0].type;
      load[i].subload.gas = u->load[0].gas;
      load[i].subload.ammo = u->load[0].ammo;
      load[i].subload.health = u->load[0].health;
    }
    return 0;
  }
  return -1;  //return -1 if unable to load unit for whatever reason
}

bool _unit::can_load_unit(_unit *u)
{
  int i = 0;
  while (i < maxload)
  {
    if (load[i].loaded == 0)
    {
      if ((type == APC) || (type == T_COPTER))
      {
        if ((u->type == INFANTRY) || (u->type == MECH))
        {
          return true;
        }
      }
      else if (type == LANDER)
      {
        if (u->basetype == LAND)
        {
          return true;
        }
      }
    }
    i++;
  }
  return false;
}

bool _unit::can_unload_unit(int n)
{
  int tx, ty, type;
  if ((load[n].loaded == 1) && (worldmap.tile[tilex][tiley].get_set() != 2))
  {  //if the transport unit is over sea, it can't unload no matter what
    type = load[n].type;
    tx = tilex + 1;
    ty = tiley;
    if (unit_unloadable_here(tilex, tiley, tx, ty, type))
    {
      return true;
    }
    tx = tilex - 1;
    if (unit_unloadable_here(tilex, tiley, tx, ty, type))
    {
      return true;
    }
    tx = tilex;
    ty = tiley + 1;
    if (unit_unloadable_here(tilex, tiley, tx, ty, type))
    {
      return true;
    }
    ty = tiley - 1;
    if (unit_unloadable_here(tilex, tiley, tx, ty, type))
    {
      return true;
    }
  }
  return false;
}

void _unit::kill()
{
  health = 0;
  exists = 0;
  init_explode(tilex, tiley);
}

void _unit::set_tiles(int tx, int ty)
{
  worldmap.tile[tilex][tiley].set_unit(-1, -1);
  tilex = tx;
  tiley = ty;
  x = tilex * MAP_TILE_SIZE;
  y = tiley * MAP_TILE_SIZE;
  worldmap.tile[tilex][tiley].set_unit(color, number);
}

void _unit::reposition(int offset_x, int offset_y)
{
	x += offset_x;
	y += offset_y;
}

int angle(int dir)
{
	switch (dir)
	{
	  case RIGHT:
  		return 0;
	    break;
	  case DOWN:
  		return 64;
	    break;
	  case LEFT:
  		return 128;
	    break;
	  case UP:
  		return 192;
	    break;
		default:
			return 0;
		  break;
  }
}

int angle_direction(int rot)
{
	rot += 32;  //an eighth of a circle
	while (rot > 256) rot -= 256;
	
	if (rot < 64) return RIGHT;
	if (rot < 128) return DOWN;
	if (rot < 192) return LEFT;
	return UP;
}
