#include "empire.h"

#define MFIRST 6
#define MLOAD 7
#define MNEW 8
#define MEDIT 9
#define MEXIT 10
#define MCHECK 11

typedef struct setunit  //the units predeployed on the map, if any
{
  int x;
  int y;
  int player;
  int type;
  int exist;
} setunit;
setunit mapunit[200];

const int numtiles[6] = {20, 10, 47, 28, 24, 72};

extern int unitmap[50][50];  //do not confuse with unitmap[200]

extern char msg[30];
extern volatile int counter;

extern DATAFILE *graphics;
extern BITMAP *back_buffer;
extern BITMAP *sprite_buffer;

extern int any_unit_here(int tx, int ty);

void draw_selection(int set, int x);
void draw_tiles(int scrollx, int scrolly, int editing, int keep_units);
void draw_tiles(int scrollx, int scrolly, int editing, int x1, int y1, int w, int h, int keep_units);
void draw_tile(int set, int pic, int x, int y);
void delete_map(char *file);
void save_map();
void draw_minimap(int x, int y, string file);
void show_minimap(int scrollx, int scrolly, int mapeditor);
void show_minimap();
void hide_minimap();
void text_box(int x, int y, int w, int h, char *ret, int leng, int spaces);
void draw_units(int scrollx, int scrolly);
void draw_unit(int num, int x, int y);
void new_unit(int x, int y, int type, int player);
void new_unit(int x, int y, int type, int player, int sound);
void clear_all_units();
void remove_unit(int n);
void remove_previous_HQ(int player, int ignorex, int ignorey, int scrollx, int scrolly);
void map_message(string message);
void arrange_mapnames();
int mfirst();
int mload();
int mnew();
int medit();
int mcheck();
int mapcolor(int z);
int get_set(int mapdat);
int get_tilenum(int mapdat);
int check_for_unit(int x, int y);
int HQ_exists(int playernum);
int base_exists(int playernum);
int munit_exists(int playernum);
int find_free_unit();
int units_for_player(int plyr);
int do_aiselect(int mouse_moved);
//text_box function courtesy of Derezo at allegro.cc, just slightly modified

float length;
float height;
int l;
int h;
char mapname[15];
int map[50][50];

extern DATAFILE *tiles;
extern DATAFILE *land;
extern DATAFILE *river;
extern DATAFILE *sea;
extern DATAFILE *shoal;
extern DATAFILE *buildings;
extern DATAFILE *p[4];
extern DATAFILE *mapunits;

_button option[31];

int mapedit()
{
  static int firstentry = 1;
  static int status = MFIRST;
  int i, j;
  if (firstentry == 1)
  {
    i = 0;
    l = 16;
    h = 12;
    while (i < 50)
    {
      j = 0;
      while (j < 50)
      {
        map[i][j] = 3;
        j++;
      }
      i++;
    }
    firstentry = 0;
  }
  switch(status)
  {
    case MFIRST:
      status = mfirst();
      break;
    case MLOAD:
      status = mload();
      break;
    case MNEW:
      status = mnew();
      break;
    case MEDIT:
      status = medit();
      break;
    case MCHECK:
      status = mcheck();
      break;
    case MEXIT:
      status = MFIRST;
      clear(back_buffer);
      create_dirty(BACK, 0, 0, 640, 480);
      return MENU;
      break;
  }
  return MAPEDIT;
}





int mfirst()
{
  static int drawfirst = 1;
  int moved = mouse_moved();
  if (drawfirst == 1)
  {
    clear_to_color(back_buffer, makecol(225, 140, 15));
    hline(back_buffer, 0, 384, 640, BLACK);
    rectfill(back_buffer, 0, 385, 640, 480, makecol(0, 120, 0));
    option[0].init(10, 455, 100, 20, "New Map");
    option[0].draw(back_buffer);
    option[1].init(120, 455, 100, 20, "Load Map");
    option[1].draw(back_buffer);
    option[2].init(520, 455, 110, 20, "Back to Menu");
    option[2].draw(back_buffer);
    option[3].init(230, 455, 120, 20, "Continue Map");
    option[3].draw(back_buffer);
    moved = 1;
    create_dirty(BACK, 0, 0, 640, 480);
    drawfirst = 0;
  }
  option[0].check(back_buffer, moved);
  option[1].check(back_buffer, moved);
  option[2].check(back_buffer, moved);
  option[3].check(back_buffer, moved);
  if (moved == 1)
  {
    draw_mouse();
  }
  if (option[0].clicked() == 1)
  {
    drawfirst = 1;
    return MNEW;
  }
  if (option[1].clicked() == 1)
  {
    drawfirst = 1;
    return MLOAD;
  }
  if ((option[2].clicked() == 1) || (key[KEY_ESC]))
  {
    drawfirst = 1;
    return MEXIT;
  }
  if (option[3].clicked() == 1)
  {
    drawfirst = 1;
    return MEDIT;
  }
  return MFIRST;
}





int mload()
{
  static int drawfirst = 1;
  static int mapok = 1;
  static int highlighted = -1;
  static int delay = 0;
  static int lscroll = 0;
  static int mapnum;
  int moved = mouse_moved();
  int i;
  int j;
  int ok;
  int mx;
  int my;
  int sel;
  int pnum;
  int a, b, c, d;
  char p[30] = "";
  static string n[50];
  if (drawfirst == 1)
  {
    clear_to_color(back_buffer, makecol(225, 140, 15));
    hline(back_buffer, 0, 384, 640, BLACK);
    rectfill(back_buffer, 0, 385, 640, 480, makecol(0, 120, 0));
    textout_centre(back_buffer, font, "Left mouse: Select", 320, 460, WHITE);
    textout_centre(back_buffer, font, "Right mouse: Delete", 320, 470, RED);
    ifstream map("maps/maplist.txt");
    if (map.bad())
    {
      textout(back_buffer, font, "Unable to load 'maplist.txt'", 5, 5, RED);
      mapok = 0;
    }
    i = 0;
    while (i < 50)
    {
      n[i] = "";
      i++;
    }
    option[0].init(10, 455, 100, 20, "Back");
    option[0].draw(back_buffer);
    rect(back_buffer, 195, 15, 370, 350, LGREY);
    rectfill(back_buffer, 196, 16, 369, 349, BLACK);
    if (mapok == 1)
    {
      i = 0;
      while (!map.eof())
      {
        getline(map, n[i]);
        sprintf(p, n[i].c_str());
        a = strlen(p) - 1;           //
        b = 0;                       //
        while (b < 4)                //
        {                            // this code removes the ".map"
          p[a] = '\0';               // from the end of the name
          a--;                       //
          b++;                       //
        }                            //
        n[i] = p;
        i++;
      }
      if (i > 22)  //if the list will go beyond the load box
      {
        option[1].init(195, 355, 85, 20, "Up");
        option[1].draw(back_buffer);
        option[2].init(285, 355, 85, 20, "Down");
        option[2].draw(back_buffer);
      }
      mapnum = i;
      i = lscroll;
      while (i < lscroll + 22)
      {
        textout(back_buffer, font, n[i].c_str(), 200, 20 + (15 * (i - lscroll)), WHITE);
        i++;
      }
    }
    create_dirty(BACK, 0, 0, 640, 480);
    moved = 1;
    drawfirst = 0;
  }
  option[0].check(back_buffer, moved);
  if (mapnum > 22)
  {
    option[1].check(back_buffer, moved);
    option[2].check(back_buffer, moved);
    if (option[1].clicked() == 1)
    {
      if (lscroll > 0)
      {
        lscroll--;
        drawfirst = 1;
      }
    }
    if (option[2].clicked() == 1)
    {
      if (lscroll + 22 < mapnum)
      {
        lscroll++;
        drawfirst = 1;
      }
    }
  }
  if (option[0].clicked() == 1)
  {
    drawfirst = 1;
    highlighted = -1;
    lscroll = 0;
    return MFIRST;
  }
  mx = mouse_x;
  my = mouse_y;
  if ((mx > 200) && (mx < 370))
  {
    if ((my > 15) && (my < 350))
    {
      sel = (my - 20) / 15 + lscroll;
      if ((sel != highlighted) && (n[sel].c_str() != ""))
      {
        strcat(p, "maps/");
        strcat(p, n[sel].c_str());
        strcat(p, ".map");
        rectfill(back_buffer, 400, 85, 620, 300, makecol(225, 140, 15));
        text_mode(-1);
        textout(back_buffer, font, n[sel].c_str(), 400, 85, BLACK);
        text_mode(0);
        create_dirty(BACK, 400, 85, 220, 215);
        draw_minimap(400, 100, p);
        highlighted = sel;
      }
      if (mouse_b & 1)
      {
        strcat(p, "maps/");
        strcat(p, n[sel].c_str());
        strcat(p, ".map");
        ok = 1;
        ifstream m(p);
        if (m.bad())
        {
          ok = 0;
        }
        if (ok == 1)
        {
          sprintf(p, n[sel].c_str());  //
          strcat(p, ".map");
          i = strlen(p) - 1;           //
          j = 0;                       //
          while (j < 4)                //
          {                            // this code removes the ".map"
            p[i] = '\0';               // from the end of the name
            i--;                       //
            j++;                       //
          }                            //
          strcpy(mapname, p);          //
          m >> l >> h >> pnum;
          i = 0;
          while (i < h)  //loading the map tiles
          {
            j = 0;
            while (j < l)
            {
              m >> map[j][i];
              j++;
            }
            i++;
          }
          i = 0;
          while (i < 200)  //loading any placed units
          {
            m >> a;
            if (a != -1)  //if there is a saved unit for this i
            {
              m >> b >> c >> d;
              new_unit(a, b, c, d, 0);  //no unit placement sound
            }
            else  //if a reads -1, there are no more saved units
            {
              i = 200;
            }
            i++;
          }
          m >> aiprofile;
          lscroll = 0;
          drawfirst = 1;
          highlighted = -1;
          return MEDIT;
        }
      }
      if (mouse_b & 2)
      {
        if (delay == 0)
        {
          sprintf(p, n[sel].c_str());
          strcat(p, ".map");
          delete_map(p);
          delay = 50;
          lscroll = 0;
          drawfirst = 1;
        }
      }
    }
  }
  if (delay > 0)
  {
    delay--;
  }
  if (moved == 1)
  {
    draw_mouse();
  }
  return MLOAD;
}





int mnew()
{
  static int drawfirst = 1;
  int moved = mouse_moved();
  int i;
  int j;
  if (drawfirst == 1)
  {
    length = 16;
    height = 12;
    clear_to_color(back_buffer, makecol(225, 140, 15));
    hline(back_buffer, 0, 384, 640, BLACK);
    rectfill(back_buffer, 0, 385, 640, 480, makecol(0, 120, 0));
    textout_centre(back_buffer, font, "Size:", 320, 140, GREEN);
    option[0].init(270, 160, 40, 15, "+");
    option[0].draw(back_buffer);
    option[1].init(270, 190, 40, 15, "-");
    option[1].draw(back_buffer);
    option[2].init(330, 160, 40, 15, "+");
    option[2].draw(back_buffer);
    option[3].init(330, 190, 40, 15, "-");
    option[3].draw(back_buffer);
    option[4].init(270, 215, 100, 20, "Create New");
    option[4].draw(back_buffer);
    option[5].init(270, 250, 100, 20, "Cancel");
    option[5].draw(back_buffer);
    sprintf(msg, "%-1d", int(length));
    textout_centre(back_buffer, font, msg, 290, 180, WHITE);
    sprintf(msg, "%-1d", int(height));
    textout_centre(back_buffer, font, msg, 350, 180, WHITE);
    create_dirty(BACK, 0, 0, 640, 480);
    moved = 1;
    drawfirst = 0;
  }
  i = 0;
  while (i < 6)
  {
    option[i].check(back_buffer, moved);
    i++;
  }
  if (option[0].mousedown() == 1)
  {
    if (length < 50)
    {
      length += .25;
      sprintf(msg, "%-1d", int(length));
      textout_centre(back_buffer, font, msg, 290, 180, WHITE);
      create_dirty(BACK, 270, 180, 40, 15);
    }
  }
  if (option[1].mousedown() == 1)
  {
    if (length > 16)
    {
      length -=.25;
    }
    else
    {
      length = 16;
    }
    rectfill(back_buffer, 270, 180, 310, 189, makecol(225, 140, 15));
    sprintf(msg, "%-1d", int(length));
    textout_centre(back_buffer, font, msg, 290, 180, WHITE);
    create_dirty(BACK, 270, 180, 40, 15);
  }
  if (option[2].mousedown() == 1)
  {
    if (height < 50)
    {
      height += .25;
      sprintf(msg, "%-1d", int(height));
      textout_centre(back_buffer, font, msg, 350, 180, WHITE);
      create_dirty(BACK, 330, 180, 40, 15);
    }
  }
  if (option[3].mousedown() == 1)
  {
    if (height > 12)
    {
      height -=.25;
    }
    else
    {
      height = 12;
    }
    rectfill(back_buffer, 330, 180, 370, 189, makecol(225, 140, 15));
    sprintf(msg, "%-1d", int(height));
    textout_centre(back_buffer, font, msg, 350, 180, WHITE);
    create_dirty(BACK, 330, 180, 40, 15);
  }
  if (option[4].clicked() == 1)
  {
    l = int(length);
    h = int(height);
    i = 0;
    while (i < l)
    {
      j = 0;
      while (j < h)
      {
        map[i][j] = 3;
        j++;
      }
      i++;
    }
    mapname[0] = '\0';
    drawfirst = 1;
    clear_all_units();
    return MEDIT;
  }
  if (option[5].clicked() == 1)
  {
    drawfirst = 1;
    return MFIRST;
  }
  if (moved == 1)
  {
    draw_mouse();
  }
  return MNEW;
}





int medit()
{
  char tempname[20] = "";
  static int drawfirst = 1;
  int scrollx;
  int scrolly;
  static float scrx = 0;
  static float scry = 0;
  static int tscroll = 0;
  static int selected = -1;
  static int set = 0;
  static int minimap = 0;
  static int minimapmx;
  static int minimapmy;
  static int gone_to_mapcheck = 0;
  static int ai_select = 0;
  int scrolled;
  int i;
  int j;
  int z;
  int temp;
  int moved = mouse_moved();
  int mx = mouse_x;
  int my = mouse_y;
  int mickeyx;
  int mickeyy;
  setunit t;
  if (drawfirst == 1)
  {
    rectfill(back_buffer, 0, 401, 640, 480, LGREY);
    option[0].init(1, 405, 24, 40, "<");
    option[0].draw(back_buffer);
    option[1].init(614, 405, 24, 40, ">");
    option[1].draw(back_buffer);
    option[20].init(1, 450, 40, 15, "Land");
    option[20].draw(back_buffer);
    option[21].init(46, 450, 50, 15, "River");
    option[21].draw(back_buffer);
    option[22].init(101, 450, 40, 15, "Sea");
    option[22].draw(back_buffer);
    option[29].init(146, 450, 50, 15, "Shoal");
    option[29].draw(back_buffer);
    option[26].init(201, 450, 80, 15, "Buildings");
    option[26].draw(back_buffer);
    option[27].init(286, 450, 50, 15, "Units");
    option[27].draw(back_buffer);
    option[23].init(560, 455, 80, 15, "Save Map");
    option[23].draw(back_buffer);
    option[24].init(475, 455, 40, 15, "Name");
    option[24].draw(back_buffer);
    option[30].init(520, 455, 35, 15, "AI");
    option[30].draw(back_buffer);    
    option[25].init(430, 455, 40, 15, "Back");
    option[25].draw(back_buffer);
    option[28].init(345, 455, 80, 15, "Check Map");
    option[28].draw(back_buffer);
    text_mode(-1);
    textout(back_buffer, font, "Left mouse: Lay Tile [+Ctrl: Remove Unit]  Right mouse: Mini-map", 106, 472, BLACK);
    text_mode(0);
    if (gone_to_mapcheck == 0)
    {
      draw_tiles(0, 0, 1, 0);
      i = 0;
      draw_selection(0, i);
      draw_units(0, 0);
      scrx = 0;
      scry = 0;
      set = 0;
      tscroll = 0;
      selected = -1;
    }
    else
    {
      scrollx = int(scrx);
      scrolly = int(scry);
      draw_tiles(scrollx, scrolly, 1, 0);
      draw_selection(set, tscroll);
      draw_units(scrollx, scrolly);
      gone_to_mapcheck = 0;
    }
    minimap = 0;
    create_dirty(BACK, 0, 0, 640, 480);
    moved = 1;
    scroll_x = 0;
    scroll_y = 0;
    position_mouse(320, 240);
    drawfirst = 0;
    ai_select = 0;
  }
  if (ai_select == 1)
  {
    ai_select = do_aiselect(moved);
    if (ai_select == 0)  //do_aiselect() just ended
    {
      scrollx = int(scrx);
      scrolly = int(scry);
      draw_tiles(scrollx, scrolly, 1, 0);
      draw_units(scrollx, scrolly);
    }
  }
  else
  {
    option[20].check(back_buffer, moved);
    option[21].check(back_buffer, moved);
    option[22].check(back_buffer, moved);
    option[23].check(back_buffer, moved);
    option[24].check(back_buffer, moved);
    option[25].check(back_buffer, moved);
    option[26].check(back_buffer, moved);
    option[27].check(back_buffer, moved);
    option[28].check(back_buffer, moved);
    option[29].check(back_buffer, moved);
    option[30].check(back_buffer, moved);
    if (option[28].clicked() == 1)
    {
      gone_to_mapcheck = 1;
      drawfirst = 1;
      return MCHECK;
    }
    if (option[25].clicked() == 1)
    {
      drawfirst = 1;
      clear_all_units();
      return MFIRST;
    }
    if (option[23].clicked() == 1)
    {
      ofstream log("maps/mapedit.log", ios::app);
      if (!log.bad())
      {
        log << "Attempting to save the map...\n";
      }
      save_map();
      if (!log.bad())
      {
        log << "Attempting to rearrange map names...\n";
      }
      arrange_mapnames();
      if (!log.bad())
      {
        log << "Save-related functions for " << mapname << " completed successfully.\n\n";
      }      
      counter = 0;
    }
    if (option[24].clicked() == 1)
    {  //"name" was clicked, go to map name input
      sprintf(tempname, mapname);
      text_box(240, 200, 160, 20, mapname, 15, 1);
      if (strcmp(mapname, "_CANCEL_") == 0)
      {  //if ESC was pressed in text_box
        sprintf(mapname, tempname);
      }
    }
    if (option[30].clicked() == 1)
    {  //"AI" was clicked, select AI profile
      ai_select = 1;
    }
    if (option[20].clicked() == 1)
    {  //switch to the land tiles
      set = 0;
      tscroll = 0;
      selected = -1;
      draw_selection(set, tscroll);
    }
    if (option[21].clicked() == 1)
    {  //switch to the river tiles
      set = 1;
      tscroll = 0;
      selected = -1;
      draw_selection(set, tscroll);
    }
    if (option[22].clicked() == 1)
    {  //switch to the sea tiles
      set = 2;
      tscroll = 0;
      selected = -1;
      draw_selection(set, tscroll);
    }
    if (option[29].clicked() == 1)
    {  //switch to the shoal tiles
      set = 3;
      tscroll = 0;
      selected = -1;
      draw_selection(set, tscroll);
    }
    if (option[26].clicked() == 1)
    {  //switch to the building tiles
      set = 4;
      tscroll = 0;
      selected = -1;
      draw_selection(set, tscroll);
    }
    if (option[27].clicked() == 1)
    {  //switch to unit placement
      set = 5;
      tscroll = 0;
      selected = -1;
      draw_selection(set, tscroll);
    }
    i = 0;
    while (i < 16)
    {
      option[i].check(back_buffer, moved);
      if (option[0].clicked() == 1)
      {
        if (tscroll > 0)
        {
          tscroll--;
          draw_selection(set, i + tscroll);
        }
      }
      if (option[1].clicked() == 1)
      {
        if (tscroll + 14 < numtiles[set])
        {
          tscroll++;
          draw_selection(set, i + tscroll);
        }
      }
      if (i > 1)
      {
        if (option[i].clicked() == 1)
        {
          selected = i + tscroll - 2;
        }
      }
      i++;
    }
    get_mouse_mickeys(&mickeyx, &mickeyy);  
    scrollx = int(scrx);
    scrolly = int(scry);
    scrolled = 0;
    if (!(mouse_b & 1))
    {  //the screen can't scroll while the mouse button is down
      if (((mx <= 0) && (mickeyx < 0)) || (key[KEY_LEFT]))
      {
        if (scrx > 0)
        {
          scrx -= .25;
          scrolled = 1;
        }
      }
      if (((mx >= 639) && (mickeyx > 0)) || (key[KEY_RIGHT]))
      {
        if (scrx + 16 < l)
        {
          scrx += .25;
          scrolled = 1;
        }
      }
      if (((my >= 479) && (mickeyy > 0)) || (key[KEY_DOWN]))
      {
        if (scry + 10 < h)
        {
          scry += .25;
          scrolled = 1;
        }
      }
      if (((my <= 0) && (mickeyy < 0)) || (key[KEY_UP]))
      {
        if (scry > 0)
        {
          scry -= .25;
          scrolled = 1;
        }
      }
    }
    if ((scrollx != int(scrx)) || (scrolly != int(scry)))
    {  //if the map has scrolled, redraw the tiles
      scrollx = int(scrx);
      scrolly = int(scry);
      draw_tiles(scrollx, scrolly, 1, 0);
      draw_units(scrollx, scrolly);
    }
    if (scrolled == 0)
    {
      scrx = int(scrx);
      scry = int(scry);
    }
    if ((mouse_b & 1) && (my < 400))
    {  //place a tile/unit on the map if the mouse is pressed or remove a unit
      i = mx / 40 + scrollx;
      j = my / 40 + scrolly;
      if ((key[KEY_LCONTROL]) || (key[KEY_RCONTROL]))
      {  //if the mouse clicks while holding ctrl, remove the unit the mouse is on
        z = check_for_unit(i, j);
        if (z != -1)
        {
          draw_tiles(scrollx, scrolly, 1, mapunit[z].x - scrollx, mapunit[z].y - scrolly, 1, 1, 0);
          remove_unit(z);
        }
      }
      else if (selected != -1)
      {
        if (set != 5)  //if it's tiles, not units being placed
        {
          z = set;
          temp = selected;
          while (z > 0)
          {
            temp += numtiles[z - 1];
            z--;
          }
          if (map[i][j] != temp)  //if the tile here is different
          {                       //from the tile to be placed
            if (set == 4)
            {
              if ((selected >= 0) && (selected <= 3))
              {  //if it's an HQ being placed, remove previous HQ for that player
                remove_previous_HQ(selected, mx, my, scrollx, scrolly);
              }
            }
            play_sound(TILEDOWN);
            map[i][j] = temp;
            draw_tile(set, selected, 40 * (i - scrollx), 40 * (j - scrolly));
            z = check_for_unit(i, j);
            if (z != -1)  //so any existing unit on this tile isn't drawn over
            {
              draw_unit(z, 40 * (i - scrollx), 40 * (j - scrolly));
            }
            create_dirty(BACK, 40 * (i - scrollx), 40 * (j - scrolly), 40, 40);
          }
        }
        else  //if a unit is being placed
        {
          new_unit(i, j, selected / 4, selected % 4);
          temp = check_for_unit(i, j);
          draw_unit(temp, 40 * (i - scrollx), 40 * (j - scrolly));
        }
      }
      moved = 1;
    }
    if (mouse_b & 2)
    {
      moved = 0;  //force the mouse to not draw if the minimap is up
      if (minimap == 0)
      {
        minimap = 1;
        minimapmx = mouse_x;
        minimapmy = mouse_y;
        create_dirty(BACK, minimapmx, minimapmy, 16, 16);  //remove the mouse from view
        show_minimap(scrollx, scrolly, 1);
      }
      position_mouse(minimapmx, minimapmy);  //mouse doesn't move if the minimap is up
    }
    else if ((!(mouse_b & 2)) && (minimap == 1))
    {
      hide_minimap();
      minimap = 0;
      moved = 1;  //put the mouse back on the screen
    }
    if (moved == 1)
    {
      scrollx = int(scrx);
      scrolly = int(scry);
      rectfill(back_buffer, 5, 470, 105, 480, LGREY);
      if (my < 400)
      {  //show the mouse's tile coordinates
        rectfill(back_buffer, 5, 470, 105, 480, LGREY);
        sprintf(msg, "X: %-1d", mx / 40 + scrollx);
        textout(back_buffer, font, msg, 5, 470, WHITE);
        sprintf(msg, "Y: %-1d", my / 40 + scrolly);
        textout(back_buffer, font, msg, 50, 470, WHITE);
      }
      create_dirty(BACK, 5, 470, 100, 10);
      draw_mouse();
    }
  }
  return MEDIT;
}





void draw_tiles(int scrollx, int scrolly, int editing, int x1, int y1, int w, int h, int keep_units)
{
  int i;
  int j;
  int z;
  int s = 0;
  int thewidth;
  int theheight;
  int minx, miny, maxx, maxy;
  if (h == 0)
  {
    if (editing == 0)
    {
      theheight = 12;
    }
    else
    {
      theheight = 10;
    }
  }
  else
  {
    if (h > 12) h = 12;  //y2 is not used in editing mode, so it would be 12 max
    theheight = h;    
  }
  if (w == 0)
  {
    thewidth = 16;
  }
  else
  {
    thewidth = w;
    if (thewidth > 16) thewidth = 16;
  }
  minx = x1 * 40;
  miny = y1 * 40;
  maxx = (x1 + thewidth) * 40;
  maxy = (y1 + theheight) * 40;
  thewidth += x1;
  theheight += y1;
  i = scrollx + x1;  
  if (i < 0) i = 0;
  while (i < scrollx + thewidth)
  {
    j = scrolly + y1;
    if (j < 0) j = 0;
    while (j < scrolly + theheight)
    {
      z = map[i][j];
      s = get_set(z);
      z = get_tilenum(z);
      draw_tile(s, z, 40 * (i - scrollx), 40 * (j - scrolly));
      if ((editing == 0) && (keep_units == 1))
      {
        z = any_unit_here(i, j);
        if (z != -1)
        {
          player[z / 100].unit[z % 100].draw(1);
        }
      }
      j++;
    }
    i++;
  }
  create_dirty(BACK, minx, miny, maxx - minx, maxy - miny);
}




void draw_tiles(int scrollx, int scrolly, int editing, int keep_units)
{
  draw_tiles(scrollx, scrolly, editing, 0, 0, 0, 0, keep_units);
}





void draw_tile(int set, int pic, int x, int y)
{
  DATAFILE *selected;
  switch(set)
  {
    case 0:
      selected = land;
      break;
    case 1:
      selected = river;
      break;
    case 2:
      selected = sea;
      break;
    case 3:
      selected = shoal;
      break;
    case 4:
      selected = buildings;
      break;
  }
  draw_sprite(back_buffer, (BITMAP *)selected[pic].dat, x, y);
}





void draw_selection(int set, int x)
{
  BITMAP *b;
  DATAFILE *selected;
  b = create_bitmap(40, 40);
  switch(set)
  {
    case 0:
      selected = land;
      break;
    case 1:
      selected = river;
      break;
    case 2:
      selected = sea;
      break;
    case 3:
      selected = shoal;
      break;
    case 4:
      selected = buildings;
      break;
    case 5:
      selected = mapunits;
      break;
  }
  int i = 0;
  while (i < 14)
  {
    if (i < numtiles[set])
    {
      if (set == 5)  //give the unit sprites' buttons a grey background
      {
        clear_to_color(b, GREY);
        option[i + 2].init(40 * i + 32 + i, 405, 40, 40, "", b);
        option[i + 2].draw(back_buffer);
      }
      option[i + 2].init(40 * i + 32 + i, 405, 40, 40, "", (BITMAP *)selected[x + i].dat);
      option[i + 2].draw(back_buffer);
    }
    else
    {
      option[i + 2].init(0, 641, 0, 0);  //basically make it unclickable
      rectfill(back_buffer, 40 * i + 32 + i, 405, 40 * i + 72 + i, 445, LGREY);
      create_dirty(BACK, 40 * i + 32 + i, 405, 40, 40);
    }
    i++;
  }
  destroy_bitmap(b);
}





void save_map()
{
  int i;
  int j;
  int q = 0;
  string fname[50];
  char name[20] = "";
  char t[39];
  if (mapname[0] != '\0')  //if mapname isn't blank
  {
    rectfill(back_buffer, 390, 446, 640, 453, LGREY);
    ofstream s("maps\\maplist.txt", ios::app);
    if (s.bad())
    {
      textout(back_buffer, font, "Unable to save to 'maplist.txt'", 390, 446, RED);
      create_dirty(BACK, 390, 446, 250, 15);
    }
    else
    {
      strcat(name, mapname);
      strcat(name, ".map");
      sprintf(t, "maps/");
      strcat(t, name);
      delete_map(name);
      s << "\n" << name;
      ofstream m(t);
      if (m.bad())
      {
        sprintf(msg, "Unable to save to %s", name);
        textout(back_buffer, font, msg, 390, 446, RED);
        create_dirty(BACK, 390, 446, 250, 15);
      }
      else
      {
        m << l << "\n";
        m << h << "\n";
        if (HQ_exists(3) == 1)  //if 4 player map
        {
          m << "4" << "\n";
        }
        else if (HQ_exists(2) == 1)  //if 3 player map
        {
          m << "3" << "\n";
        }
        else  //2 player map
        {
          m << "2" << "\n";
        }
        i = 0;
        while (i < h)  //saving the tiles
        {
          j = 0;
          while (j < l)
          {
            m << map[j][i] << " ";
            j++;
          }
          m << "\n";
          i++;
        }
        i = 0;
        while (i < 200)  //saving deployed units
        {
          if (mapunit[i].exist == 1)
          {
            m << mapunit[i].x << "\n";
            m << mapunit[i].y << "\n";
            m << mapunit[i].type << "\n";
            m << mapunit[i].player << "\n";
          }
          else
          {
            m << "-1\n";
            i = 200;
          }
          i++;
        }
        m << aiprofile;
        textout(back_buffer, font, "Save complete", 390, 446, GREEN);
        create_dirty(BACK, 390, 446, 200, 15);
      }
    }
  }
  else
  {
    textout(back_buffer, font, "Name map before saving", 390, 446, RED);
    create_dirty(BACK, 390, 446, 200, 15);
  }
}





void text_box(int x, int y, int w, int h, char *ret, int leng, int spaces)
{
  char orig[leng];
  int i = 0;
  int k;
  sprintf(orig, ret);
  k = 0;
  while (orig[k] != '\0')
  {
    i++;
    k++;
  }
  int exit = 0;
  int q = 0;
  BITMAP *bmp;
  bmp = create_bitmap(w,h);
  clear(bmp);
  rect(bmp,0,0,w-1,h-1,GREEN);
  rectfill(bmp,1,1,w-2,h-2,DGREY);
  textout_centre(bmp, font, orig, w / 2, 2, GREEN);
  draw_sprite(screen,bmp,x,y);
  clear_keybuf();
  while(exit == 0)
  {
    k = readkey();
    if(((k >> 8) == KEY_ENTER)) q = 1;
    else if(((k >> 8) == KEY_BACKSPACE) && i > 0) { i--; ret[i] = '\0'; }
    else if(((k >> 8) == KEY_BACKSPACE)) { ret[i] = '\0'; }
    else if(((k >> 8) == KEY_ESC))
    {
      q = 1;
      sprintf(ret, "_CANCEL_");
    }
    else if (((k >> 8) == KEY_SPACE) && (spaces == 0))
    {  //do nothing
    }
    else
    {
      if (i < leng)
      {
        ret[i] = k & 0xFF;
        ret[i+1] = '\0';
        i++;
      }
    }
    if (q == 1)
    {
      if (ret[0] != '\0')
      {
        exit = 1;
      }
      q = 0;
    }
    rectfill(bmp,1,1,w-2,h-2,DGREY);
    textprintf_centre(bmp,font,w/2,2,GREEN,"%s",ret);
    draw_sprite(screen,bmp,x,y);
  }
  counter = 0;
  create_dirty(BACK, x, y, w, h);
  destroy_bitmap(bmp);
}





void draw_minimap(int x, int y, string file)
{
  int i;
  int j;
  int l;
  int h;
  int z;
  int s;
  int ok = 1;
  int color = 0;
  int p;
  BITMAP *mini;
  ifstream m(file.c_str());
  if (m.bad())
  {
    ok = 0;
  }
  if (ok == 1)
  {
    m >> l >> h >> p;
    mini = create_bitmap(l * 4, h * 4);
    clear(mini);
    i = 0;
    while (i < h)
    {
      j = 0;
      while (j < l)
      {
        m >> z;
        color = mapcolor(z);
        rectfill(mini, 4 * j, 4 * i, 4 * j + 4, 4 * i + 4, color);
        s = 0;
        while (z >= numtiles[s])
        {
          z -= numtiles[s];
          s++;
        }
        if (s == 4)
        {
          if (z > 3)  //if a normal building
          {
            rect(mini, j * 4, i * 4, (j * 4) + 3, (i * 4) + 3, BLACK);
          }
          else  //if an HQ
          {
            rect(mini, j * 4, i * 4, (j * 4) + 3, (i * 4) + 3, WHITE);
          }
        }
        j++;
      }
      i++;
    }
    draw_sprite(back_buffer, mini, x, y);
    create_dirty(BACK, x, y, l * 4, h * 4);
    destroy_bitmap(mini);
  }
}





int mapcolor(int z)
{
  int BRED = makecol(200, 0, 0);
  int BGREEN = makecol(0, 200, 0);
  int BBLUE = makecol(0, 0, 200);
  int BYELLOW = makecol(200, 200, 0);
  int color;
  int t = get_tilenum(z);
  int s = get_set(z);
  if ((z == 0) || (z == 1) || ((z > 4) && (z < numtiles[0])))
  {
    color = DGREY;
  }
  else if (z == 2)
  {
    color = makecol(0, 100, 0);
  }
  else if (z == 3)
  {
    color = GREEN;
  }
  else if ((s == 2) && (t == 1))  //reef
  {
    color = makecol(200, 200, 250);
  }
  else if (z == 4)
  {
    color = makecol(190, 180, 20);
  }
  else if (s == 3)  //shoal
  {
    color = makecol(225, 220, 140);
  }
  else if (s == 4)  //buildings
  {
    if (t < 4)
    {
      switch(t % 4)  //capitals
      {
        case 0:
          color = BRED;
          break;
        case 1:
          color = BBLUE;
          break;
        case 2:
          color = BGREEN;
          break;
        case 3:
          color = BYELLOW;
          break;
      }
    }
    else
    {
      switch(t % 5)  //other buildings (which can be neutral)
      {
        case 0:
          color = BRED;
          break;
        case 1:
          color = BBLUE;
          break;
        case 2:
          color = BGREEN;
          break;
        case 3:
          color = BYELLOW;
          break;
        case 4:
          color = LGREY;
          break;
      }
    }
  }
  else
  {
    color = BLUE;
  }
  return color;
}





void delete_map(char *file)
{
  vector<string> fname;
  string temp;
  char fullfile[50];
  int i = 0;
  int j = 0;
  int min;
  int t;
  int first_entry_deleted;
  sprintf(fullfile, "maps/");
  strcat(fullfile, file);
  delete_file(fullfile);
  ifstream m("maps/maplist.txt");
  while (!m.eof())  //read all the map names in
  {
    fname.push_back();
    getline(m, fname[i]);
    i++;
  }
  delete_file("maps/maplist.txt");
  t = i;
  i = 0;
  first_entry_deleted = 0;
  ofstream n("maps/maplist.txt");
  while (i < t)
  {
    if (strcmpi(file, fname[i].c_str()) != 0)  //put all the map names back except
    {                                         //for the file that was deleted
      if (i != 0)  //if it isn't the first entry, move down a
      {            //line before adding the next map name
        if (first_entry_deleted == 0)  //special case to keep a blank space from
        {                              //being the first entry in the file
          n << "\n";
        }
        else
        {
          first_entry_deleted = 0;  //if the first entry is deleted, it will avoid
        }                           //adding the "\n" at the beginning of the file
      }                             //and then move down normally from there
      n << fname[i];
    }
    else
    {
      if (i == 0)
      {
        first_entry_deleted = 1;
      }
    }
    i++;
  }
}




void show_minimap()
{
  show_minimap(0, 0, 0);
}

void show_minimap(int scrollx, int scrolly, int mapeditor)
{
  BITMAP *m;
  m = create_bitmap(l * 4, h * 4);
  clear(m);
  int x;
  int y;
  int z;
  int s;
  y = 0;
  while (y < h)
  {
    x = 0;
    while (x < l)
    {
      rectfill(m, x * 4, y * 4, (x * 4) + 4, (y * 4) + 4, mapcolor(map[x][y]));
      z = map[x][y];
      s = get_set(z);
      z = get_tilenum(z);
      if (s == 4)
      {
        if (z > 3)  //if a normal building
        {
          rect(m, x * 4, y * 4, (x * 4) + 3, (y * 4) + 3, BLACK);
        }
        else  //if an HQ
        {
          rect(m, x * 4, y * 4, (x * 4) + 3, (y * 4) + 3, WHITE);
        }
      }
      x++;
    }
    y++;
  }  
  if (mapeditor == 1)  //show where the viewable area isif in the map editor
  {
    rect(m, (scrollx * 4) - 1, (scrolly * 4) - 1, (scrollx + 16) * 4, (scrolly + 10) * 4, BLACK);
  }
  rect(sprite_buffer, 0, 0, (l * 4) + 1, (h * 4) + 1, BLACK);
  blit(m, sprite_buffer, 0, 0, 1, 1, l * 4, h * 4);
  create_dirty(SPRITE, 0, 0, (l * 4) + 2, (h * 4) + 2);
  destroy_bitmap(m);
}





void hide_minimap()
{
  rectfill(sprite_buffer, 0, 0, (l * 4) + 2, (h * 4) + 2, TRANS);
  create_dirty(BACK, 0, 0, (l * 4) + 3, (h * 4) + 3);
}





void draw_units(int scrollx, int scrolly)
{
  int i = 0;
  while (i < 200)
  {
    if (mapunit[i].exist == 1)
    {
      if ((mapunit[i].x >= scrollx) && (mapunit[i].x < scrollx + 16))
      {
        if ((mapunit[i].y >= scrolly) && (mapunit[i].y < scrolly + 10))
        {
          draw_unit(i, (mapunit[i].x - scrollx) * 40, (mapunit[i].y - scrolly) * 40);
        }
      }
    }
    i++;
  }
}

void clear_all_units()
{
  int i = 0;
  while (i < 200)
  {
    mapunit[i].exist = 0;
    i++;
  }
}

void draw_unit(int num, int x, int y)
{
  setunit t = mapunit[num];
  int i = t.x;
  int j = t.y;
  int z = map[i][j];
  int s = get_set(z);
  z = get_tilenum(z);
  draw_tile(s, z, x, y);  //redraw the background tile to cover over any previous unit
  draw_sprite(back_buffer, (BITMAP *)mapunits[(t.type * 4) + t.player].dat, x, y);
  create_dirty(BACK, x, y, 40, 40);
}

void new_unit(int x, int y, int type, int player)
{
  new_unit(x, y, type, player, 1);
}

void new_unit(int x, int y, int type, int player, int sound)
{
  int n;
  int z = check_for_unit(x, y);
  if (z == -1)  //if no unit in this spot
  {
    n = find_free_unit();  //initialize a new one
    if (n != -1)
    {
      if (units_for_player(player) < 50)
      {
        if (sound == 1)
        {
          play_sound(UNITDOWN);
        }
        mapunit[n].exist = 1;
        mapunit[n].x = x;
        mapunit[n].y = y;
        mapunit[n].type = type;
        mapunit[n].player = player;
      }
    }
  }
  else  //if a unit is already there, redefine its attributes to what was selected
  {
    if (!((mapunit[z].type == type) && (mapunit[z].player == player)))
    {  //if a unit of the exact same type is here already, it won't do anything
      if (sound == 1)
      {
        play_sound(UNITDOWN);
      }
      mapunit[z].type = type;
      mapunit[z].player = player;
    }
  }
}

int check_for_unit(int x, int y)
{
  int i = 0;
  while (i < 200)
  {
    if (mapunit[i].exist == 1)
    {
      if ((mapunit[i].x == x) && (mapunit[i].y == y))
      {
        return i;
      }
    }
    i++;
  }
  return -1;
}

int find_free_unit()
{
  int i = 0;
  while (i < 200)
  {
    if (mapunit[i].exist == 1)
    {
      i++;
    }
    else
    {
      return i;
    }
  }
  return -1;
}

void remove_unit(int n)
{
  int i = n + 1;
  while (i < 199)
  {
    mapunit[n] = mapunit[i];
    n++;
    i++;
  }
}

void remove_previous_HQ(int player, int ignorex, int ignorey, int scrollx, int scrolly)
{
  int i = 0;
  int j;
  int z;
  int s;
  while (i < h)
  {
    j = 0;
    while (j < l)
    {
      if ((j != ignorex) || (i != ignorey))
      {
        z = map[j][i];
        s = get_set(z);
        z = get_tilenum(z);
        if ((s == 4) && (z == player))  //if it finds a previous HQ for the player
        {
          map[j][i] = 3;  //set it to a grass tile
          draw_tiles(scrollx, scrolly, 1, j - scrollx, i - scrolly, 1, 1, 0);
          z = check_for_unit(j, i);
          if (z != -1)
          {
            draw_unit(z, (j - scrollx) * 40, (i - scrolly) * 40);
          }
        }
      }
      j++;
    }
    i++;
  }
}





int mcheck()
{
  static int checked = 0;
  int x, y;
  char themessage[40];
  int i;
  if (checked == 0)
  {
    map_message("CLEAR");
    if (HQ_exists(0) == 0)
    {
      map_message("Player 1 has no HQ");
    }
    if (HQ_exists(1) == 0)
    {
      map_message("Player 2 has no HQ");
    }
    i = 0;
    while (i < 4)
    {
      if (HQ_exists(i) == 1)
      {
        if ((base_exists(i) == 0) && (munit_exists(i) == 0))
        {
          sprintf(themessage, "Player %-1d has no bases or units", i + 1);
          map_message(themessage);
        }
      }
      if ((base_exists(i) == 1) || (munit_exists(i) == 1))
      {
        if (HQ_exists(i) == 0)
        {
          sprintf(themessage, "Player %-1d has no HQ", i + 1);
          map_message(themessage);
        }
      }
      i++;
    }
    map_message("ENTER");
    checked = 1;
  }
  if (key[KEY_ENTER])
  {
    checked = 0;
    return MEDIT;
  }
  return MCHECK;
}

void map_message(string message)
{
  static int i = 0;
  text_mode(-1);
  if (message == "CLEAR")
  {
    i = 0;
    rectfill(back_buffer, 100, 100, 540, 399, GREY);
    rect(back_buffer, 100, 100, 540, 399, BLACK);
    textout_centre(back_buffer, font, "Map Errors:", 320, 120, RED);
    create_dirty(BACK, 100, 100, 441, 300);
  }
  else if (message == "ENTER")
  {
    textout_centre(back_buffer, font, "Press Enter", 320, 388, BLACK);
    create_dirty(BACK, 100, 388, 441, 14);
  }
  else
  {
    textout_centre(back_buffer, font, message.c_str(), 320, 135 + (12 * i), BLACK);
    create_dirty(BACK, 100, 134 + (12 * i), 441, 14);
    i++;
  }
  text_mode(0);
}

int HQ_exists(int playernum)
{
  int x, y, z, s;
  y = 0;
  while (y < h)
  {
    x = 0;
    while (x < l)
    {
      z = map[x][y];
      s = get_set(z);
      z = get_tilenum(z);
      if ((s == 4) && (z < 4))
      {
        if (playernum == z)
        {
          return 1;
        }
      }
      x++;
    }
    y++;
  }
  return 0;
}

int base_exists(int playernum)
{
  int x, y, z, s;
  y = 0;
  while (y < h)
  {
    x = 0;
    while (x < l)
    {
      z = map[x][y];
      s = get_set(z);
      z = get_tilenum(z);
      if (s == 4)
      {
        z -= 10;  //number of tiles before player-controlled bases
        if (playernum == z)
        {
          return 1;
        }
      }
      x++;
    }
    y++;
  }
  return 0;
}

int munit_exists(int playernum)
{
  int i = 0;
  while (i < 200)
  {
    if (mapunit[i].exist == 1)
    {
      if (mapunit[i].player == playernum)
      {
        return 1;
      }
    }
    i++;
  }
  return 0;
}

int get_set(int mapdat)
{
  int z = mapdat;
  int s = 0;
  while (z >= numtiles[s])
  {
    z -= numtiles[s];
    s++;
  }
  return s;
}

int get_tilenum(int mapdat)
{
  int z = mapdat;
  int s = 0;
  while (z >= numtiles[s])
  {
    z -= numtiles[s];
    s++;
  }
  return z;
}

int units_for_player(int plyr)
{
  int n = 0;
  int i = 0;
  while (i < 200)
  {
    if (mapunit[i].exist == 1)
    {
      if (mapunit[i].player == plyr)
      {
        n++;
      }
    }
    i++;
  }
  return n;
}

int do_aiselect(int mouse_moved)
{
  static _button opt[3];
  static int first = 1;
  int i = 0;
  if (first == 1)
  {
    position_mouse(320, 200);
    set_mouse_range(200, 150, 440, 220);
    rectfill(back_buffer, 200, 150, 440, 220, DGREY);
    rect(back_buffer, 200, 150, 440, 220, BLACK);
    text_mode(-1);
    textout_centre(back_buffer, font, "Select the map's AI profile", 320, 160, WHITE);
    switch(aiprofile)
    {
      case 0:
        textout_centre(back_buffer, font, "Current: Land", 320, 175, WHITE);
        break;
      case 1:
        textout_centre(back_buffer, font, "Current: Air", 320, 175, WHITE);
        break;
      case 2:
        textout_centre(back_buffer, font, "Current: Sea", 320, 175, WHITE);
        break;
    }
    text_mode(0);
    opt[0].init(210, 190, 70, 20, "Land");
    opt[1].init(285, 190, 70, 20, "Air");
    opt[2].init(360, 190, 70, 20, "Sea");
    create_dirty(BACK, 200, 150, 241, 121);
    opt[0].draw(back_buffer);
    opt[1].draw(back_buffer);
    opt[2].draw(back_buffer);    
    first = 0;
  }
  while (i < 3)
  {
    opt[i].check(back_buffer, mouse_moved);
    if (opt[i].clicked() == 1)
    {
      aiprofile = i;
      first = 1;
      set_mouse_range(0, 0, 640, 480);
      return 0;  //ends do_aiselect()
    }
    i++;
  }
  if (mouse_moved == 1)
  {
    draw_mouse();
  }
  return 1;  //will continue doing do_aiselect()
}

void arrange_mapnames()
{
  vector<string> fname;
  string temp;
  int i, j, min;
  i = 0;
  ifstream m("maps/maplist.txt");
  while (!m.eof())  //read all the map names in
  {
    fname.push_back();
    getline(m, fname[i]);
    i++;
  }
  i = 0;
  while (i < fname.size() - 1)
  {  //arrange filenames in alphabetical order
    min = i;
    j = i;
    while (j < fname.size())
    {
      if (strcmpi(fname[j].c_str(), fname[min].c_str()) < 0)
      {
        min = j;
      }
      j++;
    }
    if (min != i)
    {
      temp = fname[i];
      fname[i] = fname[min];
      fname[min] = temp;
    }
    i++;
  }
  m.close();
  ofstream o("maps/maplist.txt");
  i = 0;
  while (i < fname.size())
  {
    o << fname[i];
    if (i < fname.size() - 1)
    {
      o << "\n";
    }
    i++;
  }
}
