#include <xsimple.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <geolib/cproj.h>
#include "mapview.h"
#include "e00.h"

static E00MAP *map;
static POLY *polys;


static void utm2geo(double *lon, double *lat)
{
   double xm = *lon, ym = *lat;
   if (invtrans[UTM](xm, ym, lon, lat))
       exit(1);
   *lon *= R2D; *lat *= R2D;
}

static void clipcoord(double *lat, double *lon)
{
   if (*lat < minlat) *lat = minlat;
   if (*lat > maxlat) *lat = maxlat;
   if (*lon < minlon) *lon = minlon;
   if (*lon > maxlon) *lon = maxlon;
}

void drawe00(E00MAP *map, unsigned long (*getiscolor)(),
       unsigned long (*getfillcolor)())
{
   static XPoint p[MAXPOINTS];
   ARC *arc;
   POLY *ply;
   int i, j, np;
   unsigned long fillcolor, iscolor;

   if (map->fillcolor == -1)
       goto lines;

   iscolor = bgcolor;
   if (getiscolor)
       iscolor = getiscolor(map);

   for (ply = map->polys; ply; ply = ply->next) {
       if (ply->minlat > maxlat || ply->maxlat < minlat ||
               ply->minlon > maxlon || ply->maxlon < minlon)
           continue;

       fillcolor = map->fillcolor;
       if (getfillcolor)
           fillcolor = getfillcolor(map, ply);

       np = 0;
       for (i = 0; i < ply->narcs; i++) {
           if (ply->arcids[i] == 0) {
              if (np) {
                   if (np > 1)
                       fillpoly(p, np, fillcolor);
                   else
                       pset(p[0].x, p[0].y, fillcolor);
               }
               arc = map->arcs[abs(ply->arcids[++i]) - 1];
               np = 0;
               if (ply->arcids[i] < 0) {
                   for (j = arc->nnodes - 1; j > -1; j--) {
                       double lat = arc->nodes[j].lat;
                       double lon = arc->nodes[j].lon;
                       clipcoord(&lat, &lon);
                       ll2xy(lon, lat, &(p[np].x), &(p[np].y));
                       np++;
                   }
               } else {
                   for (j = 0; j < arc->nnodes; j++) {
                       double lat = arc->nodes[j].lat;
                       double lon = arc->nodes[j].lon;
                       clipcoord(&lat, &lon);
                       ll2xy(lon, lat, &(p[np].x), &(p[np].y));
                       np++;
                   }
               }
               if (np > 1)
                   fillpoly(p, np, iscolor);
               else
                   pset(p[0].x, p[0].y, iscolor);

               arc->done = TRUE;
               np = 0;
               continue;
           }
           arc = map->arcs[abs(ply->arcids[i]) - 1];
           if (ply->arcids[i] < 0) {
               for (j = arc->nnodes - 1; j > -1; j--) {
                   double lat = arc->nodes[j].lat;
                   double lon = arc->nodes[j].lon;
                   clipcoord(&lat, &lon);
                   ll2xy(lon, lat, &(p[np].x), &(p[np].y));
                   np++;
               }
           } else {
               for (j = 0; j < arc->nnodes; j++) {
                   double lat = arc->nodes[j].lat;
                   double lon = arc->nodes[j].lon;
                   clipcoord(&lat, &lon);
                   ll2xy(lon, lat, &(p[np].x), &(p[np].y));
                   np++;
               }
           }
           arc->done = TRUE;
       }
       if (np) {
           if (np > 1)
               fillpoly(p, np, fillcolor);
           else
               pset(p[0].x, p[0].y, fillcolor);
       }
   }

lines:
   setlinewidth(map->width);

   for (i = 0; i < map->narcs; i++) {
       arc = map->arcs[i];
       if (arc->done)
           continue;
       np = 0;
       for (j = 0; j < arc->nnodes; j++) {
           if (arc->nodes[j].lat < minlat || arc->nodes[j].lat > maxlat ||
                   arc->nodes[j].lon < minlon || arc->nodes[j].lon > maxlon) {
               if (np) {
                   if (np > 1)
                       poly(p, np, map->color);
                   else
                       pset(p[0].x, p[0].y, map->color);
               }
               np = 0;
               continue;
           }
           ll2xy(arc->nodes[j].lon, arc->nodes[j].lat, &(p[np].x), &(p[np].y));
           np++;
       }

       if (np) {
           if (np > 1)
               poly(p, np, map->color);
           else
               pset(p[0].x, p[0].y, map->color);
       }
   }
}

static void parsearc(FILE *f, int prec)
{
   char s[100];
   int na = 0;

   for (;;) {
       ARC *arc;
       int cnum, cid, fnode, tnode, lpoly, rpoly, nnodes;
       double lon1, lat1, lon2, lat2;
       int i, nn;

       fgets(s, 100, f);
       sscanf(s, "%d %d %d %d %d %d %d", &cnum, &cid, &fnode, &tnode,
               &lpoly, &rpoly, &nnodes);
       if (cnum == -1)
           break;

       arc = (ARC *)malloc(sizeof(ARC));
       arc->nodes = (NODE *)malloc(nnodes * sizeof(NODE));
       arc->nnodes = nnodes;
       arc->done = FALSE;
       nn = 0;

       for (i = 0; i < nnodes / ((prec == 2) ? 2 : 1); i++) {
           fgets(s, 100, f);

           if (prec == 2)
               sscanf(s, "%lf %lf %lf %lf", &lon1, &lat1, &lon2, &lat2);
           else
               sscanf(s, "%lf %lf", &lon1, &lat1);

           arc->nodes[nn].lon = lon1;
           arc->nodes[nn].lat = lat1;
           nn++;

           if (prec == 2) {
               arc->nodes[nn].lon = lon2;
               arc->nodes[nn].lat = lat2;
               nn++;
           }
       }

       if (prec == 2 && (nnodes % 2)) {
           fgets(s, 100, f);
           sscanf(s, "%lf %lf", &lon1, &lat1);
           arc->nodes[nn].lon = lon1;
           arc->nodes[nn].lat = lat1;
           nn++;
       }

       map->arcs[na++] = arc;
   }

   map->narcs = na;
}

static void parsepal(FILE *f)
{
   char s[100];
   POLY *ply, *prevply;
   int na, np;
   int first = TRUE;

   polys = prevply = NULL;
   np = 0;

   for (;;) {
       int narcs;
       double xmin, ymin, xmax, ymax;
       int a1id, a1fnode, a1ap, a2id, a2fnode, a2ap;
       int i;

       fgets(s, 100, f);
       sscanf(s, "%d %lf %lf %lf %lf", &narcs, &xmin, &ymin, &xmax, &ymax);
       if (narcs == -1)
           break;

       /* Skip universal polygon */
       if (first) {
           for (i = 0; i < narcs / 2; i++)
               fgets(s, 100, f);
           if (narcs % 2)
               fgets(s, 100, f);
           first = FALSE;
           continue;
       }

       ply = (POLY *)malloc(sizeof(POLY));
       ply->arcids = (int *)malloc(narcs * sizeof(int));
       ply->minlat = ymin; ply->maxlat = ymax;
       ply->minlon = xmin; ply->maxlon = xmax;

       na = 0;
       for (i = 0; i < narcs / 2; i++) {
           fgets(s, 100, f);
           sscanf(s, "%d %d %d %d %d %d", &a1id, &a1fnode, &a1ap,
                   &a2id, &a2fnode, &a2ap);

           ply->arcids[na++] = a1id;
           ply->arcids[na++] = a2id;
       }

       if (narcs % 2) {
           fgets(s, 100, f);
           sscanf(s, "%d %d %d", &a1id, &a1fnode, &a1ap);
           ply->arcids[na++] = a1id;
       }

       ply->narcs = na;
       ply->next = NULL;

       if (! polys)
           polys = ply;
       if (prevply)
           prevply->next = ply;
       prevply = ply;
       np++;
   }

   map->polys = polys;
   map->npolys = np;
}

static void parseprj(FILE *f)
{
   char s[100];
   char label[50];
   char proj[50], datum[50], zunits[50], units[50], spheroid[50];
   int zone;
   double xshift, yshift;
   long outdatum = 0, indatum = 0;
   long iflag;
   int i;

   for (;;) {
       fgets(s, 100, f);
       if (! strncmp(s, "EOP", 3))
           break;

       if (! strncmp(s, "Projection", 10)) {
           sscanf(s, "%s %s", label, proj);
           if (! strcmp(proj, "GEOGRAPHIC"))
               map->proj = PROJ_GEO;
           else if (! strcmp(proj, "UTM"))
               map->proj = PROJ_UTM;
           else {
               fprintf(stderr, "Unsupported projection type\n");
               exit(1);
           }
           continue;
       }
       if (! strncmp(s, "Zone", 4)) {
           sscanf(s, "%s %d", label, &zone);
           continue;
       }
       if (! strncmp(s, "Datum", 5)) {
           sscanf(s, "%s %s", label, datum);
           continue;
       }
       if (! strncmp(s, "Zunits", 6)) {
           sscanf(s, "%s %s", label, zunits);
           continue;
       }
       if (! strncmp(s, "Units", 5)) {
           sscanf(s, "%s %s", label, units);
           if (map->proj == PROJ_GEO && strcmp(units, "DD")) {
               fprintf(stderr, "Unsupported unit `%s' for "
                       "GEOGRAPHIC projection\n", units);
               exit(1);
           } else if (map->proj == PROJ_UTM && strcmp(units, "METERS")) {
               fprintf(stderr, "Unsupported unit `%s' for "
                       "UTM projection\n", units);
               exit(1);
           }
           continue;
       }
       if (! strncmp(s, "Spheroid", 8)) {
           sscanf(s, "%s %s", label, spheroid);
           if (! strcmp(spheroid, "CLARKE1866"))
               outdatum = indatum = 0;
           else if (! strcmp(spheroid, "BESSEL"))
               outdatum = indatum = 2;
           else if (! strcmp(spheroid, "GRS1980"))
               outdatum = indatum = 8;
           else if (! strcmp(spheroid, "WGS84"))
               outdatum = indatum = 12;
           else {
               fprintf(stderr, "Unsupported spheroid\n");
               exit(1);
           }
           continue;
       }
       if (! strncmp(s, "Xshift", 6)) {
           sscanf(s, "%s %lf", label, &xshift);
           continue;
       }
       if (! strncmp(s, "Yshift", 6)) {
           sscanf(s, "%s %lf", label, &yshift);
           continue;
       }
   }

   for_init(PROJECT, 0, outparm, outdatum, NULL, NULL, &iflag, fortrans);
   if (iflag)
       exit(1);

   if (map->proj == PROJ_UTM) {
       ARC *arc;
       POLY *ply;

       for (i = 0; i < 15; i++)
           inparm[i] = 0.;
       inv_init(UTM, zone, inparm, indatum, NULL, NULL, &iflag, invtrans);
       if (iflag)
           exit(1);

       for (i = 0; i < map->narcs; i++) {
           int j;
           arc = map->arcs[i];
           for (j = 0; j < arc->nnodes; j++)
               utm2geo(&(arc->nodes[j].lon), &(arc->nodes[j].lat));
       }

       for (ply = map->polys; ply; ply = ply->next) {
           utm2geo(&(ply->minlon), &(ply->minlat));
           utm2geo(&(ply->maxlon), &(ply->maxlat));
       }
   } else {
       inv_init(PROJECT, 0, inparm, indatum, NULL, NULL, &iflag, invtrans);
       if (iflag)
           exit(1);
   }
}

E00MAP *loade00(char *fname, unsigned long color, unsigned long fillcolor,
       int width, void (*parseaat)(), void (*parsepat)())
{
   FILE *f;
   char s[100];
   int arcdone = FALSE, paldone = FALSE, prjdone = FALSE, aatdone = FALSE,
           patdone = FALSE;
   char *covname;

   if (! (f = fopen(fname, "r"))) {
       perror("fopen");
       exit(1);
   }

   fgets(s, 100, f);
   if (strncmp(s, "EXP", 3)) {
       fprintf(stderr, "Not an Arc/Info export format file\n");
       exit(1);
   }

   map = (E00MAP *)malloc(sizeof(E00MAP));
   map->narcs = 0;
   map->polys = NULL;

   while (fgets(s, 100, f)) {
       char arc[5];
       int prec;

       if ((! arcdone) && (! strncmp(s, "ARC", 3))) {
           sscanf(s, "%s %d", arc, &prec);
           parsearc(f, prec); arcdone++;
       }
       if ((! paldone) && (! strncmp(s, "PAL", 3))) {
           parsepal(f); paldone++;
       }
       if ((! prjdone) && (! strncmp(s, "PRJ", 3))) {
           parseprj(f); prjdone++;
       }
       if (parseaat && (! aatdone) && strstr(s, ".AAT")) {
           parseaat(f, s, map); aatdone++;
       }
       if (parsepat && (! patdone) && strstr(s, ".PAT")) {
           parsepat(f, s, map); patdone++;
       }
   }
   fclose(f);

   map->type = E00;
   map->color = color;
   map->fillcolor = fillcolor;
   map->width = width;
   if (! (covname = strrchr(fname, '/')))
       covname = fname;
   else
       covname++;
   *(strchr(covname, '.')) = '\0';
   map->covname = (char *)malloc(strlen(covname) + 1);
   strcpy(map->covname, covname);
   map->next = NULL;
   return map;
}