/*
 * Usage: jpd2else [-jpdfile name] [-output name] 
 *                 [-format type] [-ascii type]
 *                 [-continents list] [-types list] 
 *                 [-verbose] [-silent]
 * where format type is one of NONE (default), TEXT, REZ, VMF
 * where ascii type is one of DXDY (default), INT, REAL
 *
 */

#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/file.h>

#ifdef ZLIB
#include <zlib.h>
#endif

#define BIT32 int
#define BIT16 short

#define DEFAULT_JPD_FILE "/usr/share/rmap/CIA_WDB2.jpd"
#define MAXSTROKES 6000000
/* conservative upper estimate */

#define JPD_TOTALSEG 31844
#define JPD_MAGIC "!JPD1.0\n"

#define REZ_MAGIC 0x86460346

enum { AFRICA=0, ASIA, EUROPE, NAMERICA, SAMERICA };
enum { BDY=0, PBY, RIV, CIL };
enum {NONE=0, TEXT, REZ, VMF};
enum {DX_DY=0, INT, REAL};

char *cont_name[] = { 
  "Africa", "Asia", "Europe", "North America", "South America" };

char *feature[] = {
  "int", "nat", "riv", "cil" };

int rez_type[4] = { 2, 0, 1, 3 };
int rez_rank[4][12] = { 
  { 1, 2, 3, 0, 0, 0, 0, 0,  0,  0,  0,  0 },
  { 1, 3, 0, 0, 0, 0, 0, 0,  0,  0,  0,  0 },
  { 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 13,  0 },
  { 1, 2, 3, 4, 6, 7, 8, 9, 10, 13, 14, 15 }
};

struct Header
{
  char magic[12];
  unsigned char maxlength[4];
  unsigned char index[560];
};

struct Segment_index
{
  unsigned char minlong[3];
  unsigned char maxlong[3];
  unsigned char minlat[3];
  unsigned char maxlat[3];
  unsigned char address[4];
};

static int str3toint(unsigned char *str)
{
   if (str[2]<=127)
      return (int) (str[2]<<16)|(str[1]<<8)|str[0];
   else
      return (int) (0xff<<24)|(str[2]<<16)|(str[1]<<8)|str[0];
}

static int str4toint(unsigned char *str)
{
   return (int) (str[3]<<24)|(str[2]<<16)|(str[1]<<8)|str[0];
}

void fpr4(FILE * fd, int n)
{
    fprintf(fd, "%c%c%c%c", n&255, (n>>8)&255, (n>>16)&255, n>>24);
}


void prascii(FILE *fd, int x, int y)
{
   while (x<=-648000) x += 1296000;
   while (x>648000) x -= 1296000;
   fprintf(fd, "%7.3f %8.3f", y/3600.0, x/3600.0);
}

void usage()
{
   fprintf(stderr, 
      "Usage: jpd2else [-jpdfile name] [-output name]\n"
      "                [-format type] [-ascii type] [-spacing val]\n"
      "                [-continents list] [-types list]\n"
      "                [-verbose] [-silent]\n"
      "where format type is one of NONE, TEXT (default), REZ, VMF\n"
      "and ascii type is one of DXDY (default), INT, REAL\n\n");
   exit(-1);
}

int main (argc, argv)
    int             argc;
    char          **argv;
{
#ifdef ZLIB
gzFile *ff;
#else
int ff;
#endif
FILE *fd;
char *jpd_file = NULL;
char * output = NULL;
char * list_cont = NULL, * list_types = NULL;
struct Header * header;
struct Segment_index * segment_index = NULL;
signed char * segment_buffer = NULL;
int i, j, u, v, w, verbose, format, text_output_type;
int cont, numf;
int numseg, maxlength, maxwidth, maxheight;
int readseg, printedpt, countpt, spacing;
int minlong, maxlong, minlat, maxlat, nstrokes, x, y, dx, dy;
int addr, address0, address1, address2, index1, index2;

    format = NONE;
    text_output_type = DX_DY;
    verbose = 1;
    spacing = 1;
 
    for (j=1; j<argc; j++) {
        if (j<argc-1 && !strcmp(argv[j],"-jpdfile"))
	   jpd_file = (char *)strdup(argv[++j]);
	else
        if (j<argc-1 && !strcmp(argv[j],"-output"))
	   output = (char *)strdup(argv[++j]);
        else
	if (j<argc-1 && !strcmp(argv[j],"-spacing")) {
	   spacing = atoi(argv[++j]);
	   if (spacing<=0) spacing = 1;
        } else
	if (j<argc-1 && !strcmp(argv[j], "-format")) {
	   ++j;
	   if (!strcasecmp(argv[j], "none")) format = NONE;
	   if (!strcasecmp(argv[j], "text")) format = TEXT;
	   if (!strcasecmp(argv[j], "rez")) format = REZ;
	   if (!strcasecmp(argv[j], "vmf")) format = VMF;
        }
        else
	if (j<argc-1 && !strcmp(argv[j], "-ascii")) {
	   ++j;
	   if (!strcmp(argv[j], "dxdy")) text_output_type = DX_DY;
	   if (!strcmp(argv[j], "int")) text_output_type = INT;
	   if (!strcmp(argv[j], "real")) text_output_type = REAL;
        }
	else
	if (j<argc-1 && !strcmp(argv[j], "-continents")) {
	   ++j;
	   list_cont = strdup(argv[j]);
	}
	else
	if (j<argc-1 && !strcmp(argv[j], "-types")) {
	   ++j;
	   list_types = strdup(argv[j]);
	}
	else
	   if (!strcmp(argv[j], "-verbose")) verbose = 1;
	else
	   if (!strcmp(argv[j], "-silent")) verbose = 0;
	else
	   usage();
    }

    if (!output) {
       fprintf(stderr, 
             "No -output specified, using stdout as default\n\n");
       fd = stdout;
    } else
       fd = fopen(output, "w");

    numseg = 0;
    maxlength = 0;
    maxwidth = 0;
    maxheight = 0;

    if (!jpd_file) {
	fprintf(stderr, "No input JPD file has been specified\n"
		        "Trying default %s\n\n", DEFAULT_JPD_FILE);
        jpd_file = DEFAULT_JPD_FILE;
    }        

    #ifdef ZLIB
    ff = gzopen(jpd_file, "r");
    if (!ff) {
       fprintf(stderr, "Cannot open JPD file %s !!\n", jpd_file);
       exit(-1);
    }
    #else
    ff = open(jpd_file, O_RDONLY, 0);
    if (ff==-1) {
       fprintf(stderr, "Cannot open JPD file %s !!\n", jpd_file);
       exit(-1);
    }
    #endif

    header = (struct Header *) malloc(sizeof(struct Header));

    #ifdef ZLIB
    gzread(ff, header, sizeof(struct Header));  
    #else
    read(ff, header, sizeof(struct Header));
    #endif

    if (strcmp(header->magic, JPD_MAGIC)) {
       fprintf(stderr, "File %s has wrong magic string %s\n"
                       "instead of %s . Aborting !!\n",
	               jpd_file, header->magic, JPD_MAGIC);
       exit(-2);
    }

    maxlength = str4toint(header->maxlength);
    numseg = str4toint(&header->index[556]);
    segment_index = (struct Segment_index *)
                    malloc(numseg * sizeof(struct Segment_index));
    segment_buffer = (char *) malloc(6+2*maxlength);


#ifdef ZLIB
    gzseek(ff, sizeof(struct Header), SEEK_SET);
    gzread(ff, segment_index, 
                      numseg * sizeof(struct Segment_index));
#else
    lseek(ff, sizeof(struct Header), SEEK_SET);
    read(ff, segment_index, numseg * sizeof(struct Segment_index));
#endif

    address0 = sizeof(struct Header) + numseg*sizeof(struct Segment_index);

    if (format==TEXT) {
       fprintf(fd, "%s%s\n", JPD_MAGIC, "(Text format)");
       fprintf(fd, "Number of segments for each continent/category\n");
    }

    if (format==REZ) {
       fpr4(fd, REZ_MAGIC);
       fpr4(fd, 12);
       fpr4(fd, numseg);
       addr = 12 + numseg*32;
    }

    for (u=0; u<140; u++) {
       cont = u/28; /* number of continent */
       w = u%28; 
       if (w<3) { v=0; numf=w; }
       else
       if (w<5) { v=1; numf=w-3; }
       else
       if (w<16) { v=2; numf=w-5; }
       else  { v=3; numf=w-16; }
       if (u==0)
	  index1 = 0;
       else
          index1 = str4toint(&header->index[4*(u-1)]);
       index2 = str4toint(&header->index[4*u]);
       if (format==TEXT)
          fprintf(fd, "%s, %s %d : %d\n", 
             cont_name[cont], feature[v], numf, index2-index1);
       if (format==REZ) {
          for (i=index1; i<index2; i++) {
             if (i)
 	        address1 = str4toint(segment_index[i-1].address);
             else
	        address1 = address0;
	     address2 = str4toint(segment_index[i].address);
	     nstrokes = (address2-address1-6)/2;
	     minlong = str3toint(segment_index[i].minlong);
	     maxlong = str3toint(segment_index[i].maxlong);
	     minlat = str3toint(segment_index[i].minlat);
	     maxlat = str3toint(segment_index[i].maxlat);
	     fpr4(fd, maxlat);
	     fpr4(fd, minlat);
	     fpr4(fd, maxlong);
	     fpr4(fd, minlong);
	     fpr4(fd, addr);
	     fpr4(fd, 1<<u);
	     fpr4(fd, 1<<rez_type[v]);
	     fpr4(fd, rez_rank[v][numf]);
	     addr += 12 + 8*nstrokes;
	  }
       }
    }

    readseg = 0;
    maxlength = 0;
    for (u=0; u<140; u++) {
       cont = u/28; /* number of continent */
       w = u%28; 
       if (w<3) { v=0; numf=w; }
       else
       if (w<5) { v=1; numf=w-3; }
       else
       if (w<16) { v=2; numf=w-5; }
       else  { v=3; numf=w-16; }
       
       if (list_cont && !index(list_cont, '0'+cont)) continue;
       if (list_types && !index(list_types, '0'+v)) continue;
	 
       if (u==0)
	  index1 = 0;
       else
          index1 = str4toint(&header->index[4*(u-1)]);
       index2 = str4toint(&header->index[4*u]);

       for (i=index1; i<index2; i++) {
	   if (i)
 	      address1 = str4toint(segment_index[i-1].address);
           else
	      address1 = address0;
	   address2 = str4toint(segment_index[i].address);
	   nstrokes = (address2-address1-6)/2;
	   minlong = str3toint(segment_index[i].minlong);
	   maxlong = str3toint(segment_index[i].maxlong);
	   minlat = str3toint(segment_index[i].minlat);
	   maxlat = str3toint(segment_index[i].maxlat);
           if (format==TEXT) {
              fprintf(fd, "<%d> %s (%s %d)\nJPD address: %d\n", 
                 i, cont_name[cont], feature[v], numf, address1);
              if (text_output_type == REAL)
	         fprintf(fd, "Box [%4.6f %4.6f] [%3.6f %3.6f]\nStrokes %d\n", 
		          minlong/3600.0, maxlong/3600.0,
		          minlat/3600.0, maxlat/3600.0,
                          nstrokes);
	      else
	         fprintf(fd, "Box [%d %d] [%d %d]\nStrokes %d\n", 
		          minlong, maxlong, minlat, maxlat, nstrokes);
	   }
#ifdef ZLIB
           gzseek(ff, address1, SEEK_SET);
           gzread(ff, segment_buffer, address2-address1);
#else
           lseek(ff, address1, SEEK_SET);
           read(ff, segment_buffer, address2-address1);
#endif
           x = str3toint(segment_buffer);
	   y = str3toint(&segment_buffer[3]);
           if (format==TEXT) {
              if (text_output_type == REAL)
 	         fprintf(fd, "Origin %4.6f %3.6f\n", 
		          x/3600.0, y/3600.0);
              else
	         fprintf(fd, "Origin %d %d\n", x, y);
	   }
           if (format==REZ) {
              fpr4(fd, x);
              fpr4(fd, y);
              fpr4(fd, nstrokes);
	   }
           if (format==VMF) {
	      fprintf(fd, "#%d %d\n", readseg, u+1);
	      prascii(fd, x, y);
              printedpt = 1;
              countpt = 0;
	   }
	   if (nstrokes>maxlength) maxlength = nstrokes;

	   for (j=0; j<nstrokes; j++) {
	      /* iterate and draw all the appropriate points */
	      dx = (int)segment_buffer[6+2*j];
	      dy = (int)segment_buffer[7+2*j];
              if (format==TEXT) {
	         if (text_output_type == DX_DY)
                    fprintf(fd, "%d %d\n", dx, dy);
	         else {
		    x += dx;
		    y += dy;
   		    if (text_output_type == INT)
                       fprintf(fd, "%d %d\n", x, y);
	            else
                       fprintf(fd, "%4.6f %3.6f\n", x/3600.0, y/3600.0);
		 }
              }
              if (format==REZ) {
                 fpr4(fd, dx);
                 fpr4(fd, dy);
	      }
              if (format==VMF) {
		 x += dx;
		 y += dy;
		 ++countpt;
                 if (countpt%spacing==0 || j==nstrokes-1) {
                    if (printedpt%4==0)
                       fprintf(fd, "\n");
                    else
                       fprintf(fd, "  ");
	            prascii(fd, x, y);
                    ++printedpt;
		 }
	      }
	   }
           if (format==VMF)
              fprintf(fd, "\n;\n\n");
	   ++readseg;
       }
    }

    fclose(fd);
    close(ff);

    fprintf(stderr, "Read %d segments, max length = %d\n", readseg, maxlength);
    return 0;
}
