#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <io.h>
#include <sys\stat.h>
#include <math.h>
#include <winlib/winlib.h>
#include <geolib/cproj.h>
#include <geolib/proj.h>

#include "mapplot.h"
#include "map.h"
#include "wdb-ii.h"
#include "fsbox.h"


static BOOL penUp = TRUE;
static POINT dPoints[16000];
static int nPoints;


BOOL uPenDn(VARS *v, HDC hdc, double lon, double lat)
{
       double lonRad, latRad, x, y;

       if ((lon > v->minMax.maxLon) || (lon < v->minMax.minLon) ||
                       (lat > v->minMax.maxLat) || (lat < v->minMax.minLat)) {
               if (nPoints)
                       Polyline(hdc, dPoints, 16000);
               nPoints = 0;
               return TRUE;
       }

       if (penUp) {
               if (nPoints)
                       Polyline(hdc, dPoints, 16000);
               nPoints = 0;
               penUp = FALSE;
       }

       if (v->project == PLATE_CARREE) {
               dPoints[nPoints].x = (int)(lon * 100.);
               dPoints[nPoints].y = (int)(lat * 100.);
       } else {
               lonRad = lon * D2R;
               latRad = lat * D2R;
               if (v->ForTrans[v->project](lonRad, latRad, &x, &y))
                       return FALSE;
               dPoints[nPoints].x = (int)(x * v->scale);
               dPoints[nPoints].y = (int)(y * v->scale);
       }

       if (++nPoints == 16000) {
               Polyline(hdc, dPoints, 16000);
               nPoints = 0;
       }

       return TRUE;
}


BOOL DrawWDBIIMap(VARS *v, HDC hdc, char *fileName, BOOL clear)
{
       int Ifile, segCount, idx, idy, segBufSize, olt, oln, j, k, jStroke, iSeg;
       char *dataBuf;
       BIT32 i32;
       BIT16 *segBuf;
       RECT rect;
       HBRUSH hBrush;
       HPEN hPen;
       COLORREF color;
       double lastLon = 0.;
       double lon, lat;
       char *features;
       struct stat st;
       int pos;
       BOOL retVal = TRUE;

       struct segDict {
               BIT32 segid;
               BIT32 maxLat, minLat, maxLon, minLon;
               BIT32 absAddr;
               BIT16 nBytes;
               BIT16 rank;
       } *sd, *sdBuf;

       struct segment {
               BIT32 orgX, orgY;
               BIT32 id;
               BIT16 nStrokes;
               BIT16 dummy;
       } sb;

       struct cbdHead *header;

       if ((Ifile = open(fileName, O_RDONLY | O_BINARY)) == -1) {
               MBPrintf("", "%s", strerror(errno));
               return FALSE;
       }

       ShowStatus(v->hwndStatus, 0, "Processing...");

       stat(fileName, &st);
       dataBuf = (char*)malloc(st.st_size);
       read(Ifile, dataBuf, st.st_size);
       close(Ifile);

       if (clear) {
               GetClientRect(v->hwndMain, &rect);
               hBrush = CreateSolidBrush(v->colors[6]);
               FillRect (hdc, &rect, hBrush);
               DeleteObject(hBrush);
               if (! SetupWorld(v, hdc))
                       return FALSE;
       }

       if (strstr(fileName, "cil.")) {
               color = v->colors[CLR_COAST];
               features = v->wdbShwCilFeatures;
       } else if (strstr(fileName, "bdy.")) {
               color = v->colors[CLR_COUNTRY];
               features = v->wdbShwBdyFeatures;
       } else if (strstr(fileName, "riv.")) {
               color = v->colors[CLR_RIVER];
               features = v->wdbShwRivFeatures;
       } else {
               color = 0x00000000;
       }

       hBrush = SelectObject(hdc, CreateSolidBrush(color));
       hPen = SelectObject(hdc, CreatePen(PS_SOLID, 0, color));

/*
* Check the file header for the correct magic number,
* and learn the address of the segment dictionary
*/
       header = (struct cbdHead *)dataBuf;

       if (header->magic != CBD_MAGIC) {
               MBPrintf ("", "File has bad magic number %X != %X\n",
                               header->magic, CBD_MAGIC);
               free(dataBuf);
               DeleteObject(SelectObject(hdc, hBrush));
               DeleteObject(SelectObject(hdc, hPen));
               return FALSE;
       }

/* allocate space for the segment buffer */
       segBufSize = 2 * header->segMax;
       segBuf = (BIT16 *) malloc (50 + segBufSize);

/* allocate space for the segment dictionary */
       sdBuf = (struct segDict *) malloc (100 + (header->segSize));
       sd = sdBuf;
       sd++;

/* Get the segment dictionary (it's at the end of the file) */
       memcpy(sd, dataBuf + header->dictAddr, header->segSize);

/*
* Now look at each segment and decide if we're
* going to keep it or not
*/
       segCount = header->segCount;
       nPoints = 0;

       sd = sdBuf;
       for (iSeg = 1; iSeg <= segCount; iSeg++) {
               sd++;           /* does this really work? wow! */

               if (! features[sd->rank])
                       continue;

               pos = sd->absAddr;
               memcpy(&sb, dataBuf + pos, sizeof(sb));
               pos += sizeof(sb);

               if (sd->nBytes > segBufSize) {
                       MBPrintf("", "Segment %d needs %d bytes; buffer limit is %d.\n", iSeg, sd->nBytes, segBufSize);
                       retVal = FALSE;
                       goto end;
               }

               memcpy(segBuf, dataBuf + pos, sd->nBytes);

               k = 0;

               oln = sb.orgX;
               olt = sb.orgY;
               lon = oln / 3600.;
               lat = olt / 3600.;
               penUp = TRUE;
               if (! uPenDn(v, hdc, lon, lat)) {
                       retVal = FALSE;
                       goto end;
               }
               lastLon = lon;

               for (jStroke = 1; jStroke <= sb.nStrokes; jStroke++) {
                       if (segBuf[k] & SHORTFLAG) {
/* Flag bit on: unpack a 16-bit field into dx and dy */
                               i32 = segBuf[k++];
                               if (i32 > 0)
                                       i32 &= ~SHORTFLAG;
                               idy = i32 & 0xFF;
                               if (idy & 0x80)
                                       idy |= ~0xFF;   /* extend sign */
                               idx = i32 >> 8;
                               if (idx & 0x80)
                                       idx |= ~0xBF;   /* extend sign */
                       } else {
/* Flag bit off: take dx and dy from 32-bit fields. */
                               idx = segBuf[k++];
                               if (idx < 0)
                                       idx |= SHORTFLAG;
                               idx = (idx << 16) | (unsigned short) segBuf[k];
                               k++;

                               idy = segBuf[k];
                               k++;
                               if (idy < 0)
                                       idy |= SHORTFLAG;
                               idy = (idy << 16) | segBuf[k];
                               k++;
                       }

                       oln = (oln + idx);
                       olt = (olt + idy);
                       lon = oln / 3600.;
                       lat = olt / 3600.;

                       if (fabs(lastLon - lon) > 180.)
                               penUp = TRUE;

                       if (! uPenDn(v, hdc, lon, lat)) {
                               retVal = FALSE;
                               goto end;
                       }
                       lastLon = lon;
               }
       }

       if (nPoints)
               Polyline(hdc, dPoints, nPoints);

end:
       free(sdBuf);
       free(segBuf);
       free(dataBuf);
       DeleteObject(SelectObject(hdc, hBrush));
       DeleteObject(SelectObject(hdc, hPen));
       ShowStatus(v->hwndStatus, 0, "Done");
       return retVal;
}


void DrawWDBIIMaps(VARS *v, HDC hdc)
{
       WDBII_MAP *wdbIIMap;
       BOOL clear = TRUE;

       for (wdbIIMap = v->wdbIIMaps; wdbIIMap; wdbIIMap = wdbIIMap->next) {
               if (! DrawWDBIIMap(v, hdc, wdbIIMap->name, clear))
                       return;
               clear = FALSE;
       }

       if (v->shwFeatures & SHW_GRID)
               DrawGrid(v, hdc);
}


void DoAddWDBIIMap(VARS *v, char *name)
{
       WDBII_MAP *wdbIIMap, *p;

       wdbIIMap = (WDBII_MAP *)malloc(sizeof(WDBII_MAP));
       strcpy(wdbIIMap->name, name);
       wdbIIMap->next = NULL;

       if (v->wdbIIMaps) {
               for (p = v->wdbIIMaps; p->next; p = p->next);
               p->next = wdbIIMap;
       } else
               v->wdbIIMaps = wdbIIMap;

       v->wdbIIMode = TRUE;
}


BOOL AddWDBIIMap(VARS *v)
{
       WDBII_MAP *wdbIIMap, *p;
       char szFileName[256], szTitleName[256];
       char curDir[256];

       szFileName[0] = '\0';
       szTitleName[0] = '\0';

       if (*(v->wdbIIDir) != '\0')
               FileInitialize(0, v->wdbIIDir);
       else
               FileInitialize(0, NULL);

       if (! FileOpenDlg(v->hwndMain, szFileName, szTitleName))
               return FALSE;

       DoAddWDBIIMap(v, szFileName);

       GetCurrentDirectory(256, (LPTSTR)curDir);
       if (strcmp(v->wdbIIDir, curDir))
               strcpy(v->wdbIIDir, curDir);

       InvalidateRect(v->hwndMain, NULL, FALSE);
       UpdateWindow(v->hwndMain);
       return TRUE;
}


void DeleteWDBIIMaps(VARS *v)
{
       char *p;

       while (v->wdbIIMaps) {
               p = (char *)(v->wdbIIMaps);
               v->wdbIIMaps = v->wdbIIMaps->next;
               free(p);
   }

       v->wdbIIMode = FALSE;
}


BOOL WDBIILoaded(VARS *v, char *name)
{
       WDBII_MAP *p;

       for (p = v->wdbIIMaps; p; p = p->next) {
               if (strstr(p->name, name))
                       return TRUE;
       }
       return FALSE;
}