#include <errno.h>
#include "MapIO.h"

void
saveMapPath(char *path)
{
  FILE *fp;
  size_t new_size, written;
  Uint16 *compressed_map;
  char *err;
  SDL_RWops *rwop;

  printf("Saving map %s\n", path);
  fflush(stdout);

  if(!(fp = fopen(path, "wb"))) {
    err = strerror(errno);
    fprintf(stderr, "Can't open file for writing: %s\n", err);
    fflush(stderr);
    return;
  }

  rwop = SDL_RWFromFP(fp, 1);

  // write the header
  SDL_WriteLE16(rwop, map.w);
  SDL_WriteLE16(rwop, map.h);

  // compression step 1
  printf("Compressing...\n");
  compressed_map = compressMap(&new_size);
  fprintf(stderr, "Compressed map. old_size=%ld new_size=%ld\n",
          (long int) (LEVEL_COUNT * map.w * map.h), (long int) new_size);
  fflush(stderr);
  // write out and further compress in step 2
  written = compress(compressed_map, new_size, rwop);
  fprintf(stderr,
          "Compressed map step2. Written %ld ints. Compression ration: %f.2%%\n",
          (long int) written,
          (float) written / ((float) (LEVEL_COUNT * map.w * map.h) / 100.0));
  fflush(stderr);

  // Does this close the file? It should according to the constructor...
  SDL_RWclose(rwop);
  free(compressed_map);
}

void
saveMap()
{
  char path[PATH_SIZE];
  sprintf(path, xstr(BASE_DIR) PATH_SEP MAPS_DIR PATH_SEP "%s.dat", map.name);
  saveMapPath(path);
}

// call these after initMap()!
int
loadMap(int draw_map)
{
  char path[PATH_SIZE];
  sprintf(path, xstr(BASE_DIR) PATH_SEP MAPS_DIR PATH_SEP "%s.dat", map.name);
  return loadMapPath(path, draw_map);
}

int
loadMapPath(char *path, int draw_map)
{
  FILE *fp;
  size_t size;
  Uint16 *read_buff;
  int count_read;
  char *err;
  SDL_RWops *rwop;
  int x, y, i;

  printf("Loading map %s\n", path);
  fflush(stdout);
  if(!(fp = fopen(path, "rb"))) {
    err = strerror(errno);
    fprintf(stderr, "Can't open file for reading: %s\n", err);
    fflush(stderr);
    return 0;
  }
  rwop = SDL_RWFromFP(fp, 1);

  // read the header
  map.w = SDL_ReadLE16(rwop);
  map.h = SDL_ReadLE16(rwop);

  printf("map dimensions %dx%d\n", map.w, map.h);
  fflush(stdout);

  // compression step 1: read compressed data from disk
  // FIXME: what would be nicer is to only allocate as much mem as used on disk.
  size = LEVEL_COUNT * map.w * map.h;
  printf("size %u\n", size);
  fflush(stdout);
  if(!(read_buff = (Uint16 *) malloc(sizeof(Uint16) * size))) {
    fprintf(stderr, "Out of memory on map read.");
    fflush(stderr);
    exit(0);
  }
  count_read = decompress(read_buff, size, rwop);
  fprintf(stderr, "read %d ints\n", count_read);
  fflush(stderr);
  // Does this close the file? It should according to the constructor...
  SDL_RWclose(rwop);

  // step 2: further uncompress
  decompressMap(read_buff);
  free(read_buff);

  // clean map... delete this...
  fprintf(stderr, "*** Debug code: removing stuff in LEVEL_FORE.\n");
  for(y = 0; y < map.h; y++) {
    for(x = 0; x < map.w; x++) {
      i = x + (y * map.w);
      if(map.image_index[LEVEL_FORE][i] == 0)
        map.image_index[LEVEL_FORE][i] = EMPTY_MAP;
    }
  }

  resetCursor();
  if(draw_map)
    drawMap();
  return 1;
}










int
convertMap(char *from, char *to)
{
  FILE *fp;
  int *buff;
  int count_read;
  char *err;
  int w, h, i;

  printf("Reading map to be converted %s\n", from);
  fflush(stdout);
  if(!(fp = fopen(from, "rb"))) {
    err = strerror(errno);
    fprintf(stderr, "Can't open file for reading: %s\n", err);
    fflush(stderr);
    free(err);
    return 0;
  }
  // read the header
  fread(&(w), sizeof(w), 1, fp);
  fread(&(h), sizeof(h), 1, fp);

  if((buff = (int *) malloc(sizeof(int) * w * h)) == NULL) {
    fprintf(stderr, "Can't allocate memory!\n");
    fflush(stderr);
    return 0;
  }
  count_read = fread(buff, sizeof(int), w * h, fp);
  fprintf(stderr, "read %d ints\n", count_read);
  fflush(stderr);
  fclose(fp);

  // now throw away the second WORD and resave
  printf("Saving converting map %s\n", to);
  fflush(stdout);
  if(!(fp = fopen(to, "wb"))) {
    err = strerror(errno);
    fprintf(stderr, "Can't open file for writing: %s\n", err);
    fflush(stderr);
    free(err);
    return 0;
  }
  // read the header
  fwrite(&w, sizeof(Uint16), 1, fp);
  fwrite(&h, sizeof(Uint16), 1, fp);
  for(i = 0; i < count_read; i++) {
    fwrite(buff + i, sizeof(Uint16), 1, fp);
  }
  fclose(fp);
  free(buff);
  return 1;
}

/** Remove unnecesary EMPTY_MAPs. For example a 4 tile wide stone becomes a 1 int number.
	return new number of elements in new_size. (so num of bytes=new_size * sizeof(int)).
	caller must free returned pointer.
	call this method before calling Utils.compress(). This prepares the map
	for better compression by understanding the its structure. This doesn't 
	compress the map that much, but combined with Utils.compress() map files
	can go from 12M to 14K!
*/
Uint16 *
compressMap(size_t * new_size)
{
  Uint16 *q;
  Uint16 n;
  int level, i, x, y;
  size_t t = 0;

  if(!(q = (Uint16 *) malloc(sizeof(Uint16) * map.w * map.h * LEVEL_COUNT))) {
    fprintf(stderr, "Out of memory in compressMap.");
    fflush(stderr);
    exit(-1);
  }
  for(level = 0; level < LEVEL_COUNT; level++) {
    for(y = 0; y < map.h; y++) {
      for(x = 0; x < map.w;) {
        i = x + (y * map.w);
        n = map.image_index[level][i];
        *(q + t) = n;
        t++;
        if(n != EMPTY_MAP) {
          // skip ahead
          x += images[n]->image->w / TILE_W;
        } else {
          x++;
        }
      }
    }
  }
  *new_size = t;
  return q;
}

/**
   Decompress map by adding back missing -1-s. See compressMap() for
   details.
 */
void
decompressMap(Uint16 * p)
{
  int level, i, x, y, r;
  Uint16 n;
  size_t t = 0;

  for(level = 0; level < LEVEL_COUNT; level++) {
    for(y = 0; y < map.h; y++) {
      for(x = 0; x < map.w;) {
        n = *(p + t);
        t++;
        i = x + (y * map.w);
        map.image_index[level][i] = n;
        x++;
        if(n != EMPTY_MAP) {
          for(r = 1; r < images[n]->image->w / TILE_W && x < map.w; r++, x++) {
            map.image_index[level][i + r] = EMPTY_MAP;
          }
        }
      }
    }
  }
}
