/* pla2gif.c
 * Written by Jouni Aro <jaro@hut.fi> in December 1993
 *
 * This is a converter program that converts a planet file
 * created by 'planet2' into a .gif-file.
 *
 * It uses GifLib library v1.2 written by Gershon Elber, 1991
 *
 * usage: pla2gif [-p colormap/altitudefile] [infile] [outfile]
 * if no files are given stdin/stdout will be used
 *
 * The colormaps should include one row per colour and altitude
 * range. The rows are read in as following:
 * red green blue alt
 * where the first three values define the color to be used for
 * altitudes less than the fourth value. The fourth value can also
 * be 'BG', which defines that colour to be used as background colour,
 * or 'ICE', which will be used for altitudes higher than the
 * highest altitude value.
 * The rows should be in ascending order regarding their altitudes
 * Note also that pure colormaps are also accepted, so the altitude
 * value is actually optional.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#ifdef __MSDOS__
#include <io.h>
#include <fcntl.h>

extern int getopt(int ac, char **av, char *optstr);

#else
#include <unistd.h>
#endif

#include "gif_lib.h"

extern char *VersionStr;
extern int alt_decode(FILE *fp);

#define MAP_COMMENT_CHAR ':'
#define DEBUG 0

#define MAXALT   32767
#define MINALT  -32768

#ifdef __MSDOS__
extern unsigned int
    _stklen = 16384;			     /* Increase default stack size. */
#endif /* __MSDOS__ */

static GifColorType DEFPalette[] =           /* Default color map. */
{
    {   0,   0,   0 },   /* 0. Black */
    {   0,   0, 128 },   /* 1. Deepest sea */
    {  28,  28, 168 },
    {  56,  56, 208 },
    {  84,  84, 255 },   /* 4. Lowest sea */
    {   0, 168,   0 },   /* 5. Lowest land */
    { 127, 208,  40 },
    { 255, 255,  84 },   /* 7. Midland */
    { 224, 196,  56 },
    { 196, 140,  32 },
    { 168,  84,   0 },   /* 10. Mountains */
    { 240, 240, 240 },   /* 11. Icecaps */
    { 168,   0,   0 },   /* 12. Dark Red */
    {  84,   0,   0 },   /* 13. another */
    { 128, 128, 128 },   /* 14. Grey */
    { 255, 255, 255 },   /* 15. White */
};

typedef struct LevelType {
    int alt;
    int color;
} LevelType;

static LevelType DEFLevels[] = { 
    {-1000, 1}, 
    { -500, 2},
    { -200, 3},
    {    0, 4},
    {  150, 5},
    {  300, 6},
    {  600, 7},
    {  900, 8},
    { 1200, 9},
    { 1500, 10}
};

char *prog_name;

void fatal(char *msg)
{
  fprintf(stderr, "\n%s: %s\n", prog_name, msg);
  exit(1);
}

main(int ac, char **av)
{
  GifFileType *gif;
  GifPixelType *pixline;
  int levels;
  LevelType *levelmap;
  GifColorType *colormap;
  int colors, BGColor, ICEColor;
  int BitsPerPixel;
  int r, g, b;

  FILE *in, *map;
  int i, j, c, d, l, alt;
  char line[80], version[20], str[20];
  int  width, height, view;
  long seed;

  extern int optind;
  extern char *optarg;
  int opt;

#ifdef __MSDOS__
  int quiet = 0;
  if ((prog_name = strrchr(av[0], '\\')) == NULL)
    prog_name = av[0];
  else
    prog_name++;
#else
  int quiet = 1;
  prog_name = av[0];
#endif


  colormap = DEFPalette;
  colors = sizeof(DEFPalette)/sizeof(GifColorType);
  BGColor = 0;
  levelmap = DEFLevels;
  levels = sizeof(DEFLevels)/sizeof(LevelType);
  ICEColor = levelmap[levels-1].color+1;

/* Parse arguments */
  optind = 1;
  do {
    opt = getopt(ac, av, "p:q:h");
    
    switch(opt) {
    case -1 : break;

/* -p colormap/altitudefile */
    case 'p':
      if ((map = fopen(optarg, "r")) == NULL)
        fatal("Couldn't open levelsfile");

      if(((levelmap = (LevelType *) malloc(sizeof(LevelType) * 256))
                   == NULL)
      || ((colormap = (GifColorType *) malloc(sizeof(GifColorType) * 256)) 
                   == NULL))
        fatal("Need some more memory.");

      i = 0;
      l = 0;
      ICEColor = -1;
      while (!feof(map) && i < 256) {
        fgets(line, 80, map);
        if (line[0] == MAP_COMMENT_CHAR)
          continue;

        j = sscanf(line, "%d %d %d %s", &r, &g, &b, str);
        if (j < 3) /* Don't care of almost empty lines */
          continue;

        colormap[i].Red   = r;
        colormap[i].Green = g;
        colormap[i].Blue  = b;

        if (j > 3) {
          if (strcmp(str, "BG") == 0) /* background color */
            BGColor = i;
          else if (strcmp(str, "ICE") == 0) /* 'ice' color */
            ICEColor = i;
          else if ( ((alt = atoi(str)) != 0) || (*str == '0')) {
            /* altitude defined after color code: use it */
            levelmap[l].alt   = alt;
            levelmap[l].color = i;
            l++;
          }
        }
        i++;
      }
      colors = i;
      /* colormaps without altitude values: a uniform scale */
      if (l == 0) {
        if (!quiet) 
          fprintf(stderr, "%s: No altitudes marked, making a uniform scale\n", 
                  optarg);
        levelmap[0].color = 0;
        levelmap[0].alt   = -1000;
        levelmap[1].color = colors-2;
        levelmap[1].alt   = 1000;
        levels = 2;
      }
      else 
        levels = l;
      if (ICEColor < 0)
        ICEColor = colors-1;
      
      fclose(map);
      if (!quiet) 
        fprintf(stderr, "%s: %d colors, %d levels\n", optarg, colors, levels);
      break;
/* -q quiet */
    case 'q':
      quiet = atoi(optarg);
    case 'h':
    default: 
      fprintf(stderr, "\
Usage: %s [-p colormap/altitudefile] [-h] [infile] [outfile]", prog_name);
      exit(1);
    }
  } while (opt != -1);


  if (optind < ac) {
    if((in = fopen(av[optind], "rb")) == NULL)
      fatal("Couldn't open infile.");
    optind++;
  }
  else {
    in = stdin;
#ifdef __MSDOS__
/* Make sure it's in binary form */
    setmode(0, O_BINARY); 
#endif
  }

  if (optind < ac) {
    if((gif = EGifOpenFileName(av[optind], FALSE)) == NULL)
      fatal("Couldn't open outfile.");
    optind++;
  }
  else
    if((gif = EGifOpenFileHandle(1)) == GIF_ERROR)
      fatal("Couldn't open stdout!?.");

/* Read header info from planet file */
  fgets(line, 80, in);
  if (((sscanf(line, "%s %ld %dx%d", 
               version, &seed, &width, &height) != 4)
      || (strcmp(version, VersionStr) != 0)))
    fatal("Not a correct planet file!");

  line[strlen(line)-1] = '\0'; /* strip '\n' */
  if (! quiet) 
    fprintf(stderr, "%s: %s:     ", prog_name, line);

/* Prepare the gif-encoder */
  if ((pixline = (GifPixelType *) malloc(width*sizeof(GifPixelType))) == NULL)
    fatal("Not much memory to do this.");

  i = colors-1;
  j = 0;
  while (i > 0) {
    i >>= 1;
    j++;
  }
  BitsPerPixel = j;

  if (EGifPutScreenDesc(gif, width, height, BitsPerPixel ,
			BGColor, BitsPerPixel, colormap) == GIF_ERROR)
    fatal("Failed putting screen descriptor.");

  if (EGifPutImageDesc(gif, 0, 0, width, height, FALSE, 1,
			 NULL) == GIF_ERROR)
    fatal("Failed putting image descriptor.");


/* Start converting */
  for (j = 0; j < height; j++) {
    for (i = 0; i < width; i++) {

      if(feof(in))      /* This makes incomplete maps possible to be viewed */
        c = BGColor;
      else {
        alt = alt_decode(in);
        if (alt == MAXALT)
          c = BGColor; /* BACKGROUND */
        else {
          /* if alt > levelmap[levels-2] then it's highest level */
          c = ICEColor;
          for(d = 0; d < levels; d++)
            if (alt < levelmap[d].alt) {
              c = d;
              break;
            }
          if (c != ICEColor) {
            d = levelmap[c].color-levelmap[c-1].color;
            if((c > 0) && (d > 1))
              c = levelmap[c].color -
                  d*(levelmap[c].alt-alt)/(levelmap[c].alt-levelmap[c-1].alt);
            else
              c = levelmap[c].color;
          }
        }
      }

      pixline[i] =(GifPixelType) c;
    }
    if (EGifPutLine(gif, pixline, width) == GIF_ERROR)
      fatal("Write failed.");
    if (!quiet) 
      fprintf(stderr,"\b\b\b\b%-4d", j);
  }
    
  free((char *) pixline);

  if(EGifCloseFile(gif) == GIF_ERROR)
    fatal("Couldn't close file");
  if (!quiet) 
    fputc('\n',stderr);
  fclose(in);

  return 0;
}
