/*
* MapPlot
*
* Simple map drawing program for MWDB-II/WDB-II data
*
*/

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <io.h>
#include <math.h>
#include <winlib/winlib.h>

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


BOOL ReadMap(VARS *v, char *name)
{
       FILE *f;
       HDR h;
       DPOINT p;
       MAP_POLY *newPoly;
       char tmp[1024];
       int i;

       sprintf(tmp, "d:/Graphics/GIS/MWDB-II/mw%s.vec", name);
       if (! (f = fopen(tmp, "rb"))) {
               MBPrintf("MapPlot", "Couldn't open data file '%s.vec'", name);
               return FALSE;
       }

       while (fread((void *)&h, (size_t)sizeof(HDR), (size_t)1, f) == 1) {
               if ((int)h.np == 0) {
                       fclose(f);
                       return TRUE;
               }
               newPoly = (MAP_POLY *)malloc(sizeof(MAP_POLY));
               newPoly->feature = (char)h.feature;
               newPoly->nPoints = (unsigned short)h.np;
               if ((int)h.np == 0) {
                       MBPrintf("", "fan");
                       exit(1);
               }
               newPoly->points = (MAP_POINT *)malloc(sizeof(MAP_POINT) * (int)h.np);
               newPoly->next = NULL;

               for (i = 0; i < (int)h.np; i++) {
                       if (fread ((void *)&p, (size_t)sizeof(DPOINT),
                                       (size_t)1, f) != 1) {
                               fclose(f);
                               MBPrintf("MapPlot", "Error reading data");
                               return FALSE;
                       }
                       newPoly->points[i].lon = p.lon / 60.0 * 100;
                       newPoly->points[i].lat = p.lat / 60.0 * 100;
               }

               if (! v->mapData)
                       v->mapData = newPoly;
               if (v->prevPoly)
                       v->prevPoly->next = newPoly;
               v->prevPoly = newPoly;
       }

       fclose(f);
       return TRUE;
}


BOOL ReadMaps(VARS *v)
{
       char *names[] = {"coast", "nation", "state", "island",
                       "lake", "river", NULL};
       char *name;
       int i;

       for (i = 0; name = names[i]; i++) {
               if (! ReadMap(v, name))
                       return FALSE;
       }
       return TRUE;
}


BOOL DrawMap(VARS *v, HDC hdc)
{
       MAP_POLY *mapPoly;
       POINT *dPoints;
       RECT rect;
       HBRUSH hBrush;
       HPEN hPen;
       COLORREF color;
       HRGN hRgn;
       double origXOff, origYOff;
       int i;

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

       GetClientRect(v->hwndMain, &rect);

       hRgn = SelectObject(hdc, CreateRectRgn(0, 0, rect.right,
                       rect.bottom - (v->statusHeight + 42)));
       SelectClipRgn(hdc, hRgn);

       hBrush = CreateSolidBrush(v->colors[0]);
       FillRect (hdc, &rect, hBrush);
       DeleteObject(hBrush);

       SetMapMode(hdc, MM_ISOTROPIC);
       SetWindowExtEx(hdc, 18000, 9000, NULL);
       SetViewportExtEx(hdc, (rect.right / 2) * v->zoom,
                       (-rect.bottom / 2) * v->zoom, NULL);
       SetViewportOrgEx(hdc, rect.right / 2,
                       (rect.bottom - (v->statusHeight + 42)) / 2, NULL);

       origXOff = v->origLon * 100;
       origYOff = v->origLat * 100;

       for (mapPoly = v->mapData; mapPoly; mapPoly = mapPoly->next) {
               switch (mapPoly->feature) {
                       case FEAT_COAST:
                               if (! (v->shwFeatures & SHW_COAST))
                                       continue;
                               color = v->colors[1];
                               break;

                       case FEAT_COUNTRY:
                               if (! (v->shwFeatures & SHW_COUNTRY))
                                       continue;
                               color = v->colors[2];
                               break;

                       case FEAT_STATE:
                               if (! (v->shwFeatures & SHW_STATE))
                                       continue;
                               color = v->colors[3];
                               break;

                       case FEAT_ISLAND:
                               if (! (v->shwFeatures & SHW_ISLAND))
                                       continue;
                               color = v->colors[4];
                               break;

                       case FEAT_LAKE:
                               if (! (v->shwFeatures & SHW_LAKE))
                                       continue;
                               color = v->colors[5];
                               break;

                       case FEAT_RIVER:
                               if (! (v->shwFeatures & SHW_RIVER))
                                       continue;
                               color = v->colors[6];
                               break;

                       default:
                               continue;
               }

               dPoints = (POINT *)malloc(sizeof(POINT) * (int)mapPoly->nPoints);

               for (i = 0; i < (int)mapPoly->nPoints; i++) {
                       dPoints[i].x = mapPoly->points[i].lon - origXOff;
                       dPoints[i].y = mapPoly->points[i].lat - origYOff;
               }

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

               if (! Polyline(hdc, dPoints, (int)mapPoly->nPoints)) {
                       DeleteObject(SelectObject(hdc, hBrush));
                       DeleteObject(SelectObject(hdc, hPen));
                       free(dPoints);
                       MBPrintf("MapPlot", "Polygon overflow: %d points",
                                       mapPoly->nPoints);
                       return FALSE;
               }

               DeleteObject(SelectObject(hdc, hBrush));
               DeleteObject(SelectObject(hdc, hPen));
               free(dPoints);
       }

       ShowStatus(v->hwndStatus, 0, "Done");
       DeleteObject(SelectObject(hdc, hRgn));
       return TRUE;
}


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

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


LRESULT CALLBACK EditProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
       static VARS *v = NULL;
       static WNDPROC oldProc = NULL;
       char s[80];
       int i;
       static int shift = FALSE;

       if (! v)
               v = (VARS *)GetWindowLong(GetParent(hWnd), 0);

       if (! oldProc)
               oldProc = (WNDPROC)GetWindowLong(hWnd, GWL_USERDATA);

       switch(msg) {
               case WM_SETFOCUS:
                       v->oldFocus = hWnd;
                       break;

               case WM_KEYDOWN:
                       switch (wParam) {
                               case VK_RETURN:
                                       GetWindowText(v->hwndEdit[0], s, 80);
                                       v->origLon = atof(s);
                                       GetWindowText(v->hwndEdit[1], s, 80);
                                       v->origLat = atof(s);
                                       GetWindowText(v->hwndEdit[2], s, 80);
                                       v->zoom = atof(s);
                                       InvalidateRect(v->hwndMain, NULL, FALSE);
                                       UpdateWindow(v->hwndMain);
                                       return 0;

                               case VK_TAB:
                                       for (i = 0; i < 3; i++)  {
                                               if (GetFocus() == v->hwndEdit[i])
                                                       break;
                                       }
                                       if (! shift)
                                               i = (i == 2) ? 0: i + 1;
                                       else
                                               i = (i == 0) ? 2: i - 1;
                                       SetFocus(v->hwndEdit[i]);
                                       return 0;

                               case VK_SHIFT:
                                       shift = TRUE;
                                       return 0;
                       }
                       break;

               case WM_KEYUP:
                       switch (wParam) {
                               case VK_SHIFT:
                                       shift = FALSE;
                                       return 0;
                       }
                       break;
       }

       return CallWindowProc(oldProc, hWnd, msg, wParam, lParam);
}


LRESULT CALLBACK MainWndProc(HWND hwnd, UINT msg, WPARAM wParam,
               LPARAM lParam)
{
       static VARS *v;
       HDC hdc;
       PAINTSTRUCT ps;
       RECT rect;
       int i, offs = 5;
       BOOL r;
       HFONT hfnt;
       WNDPROC oldProc;
       static CHOOSECOLOR cc ;
       static COLORREF crCustColors[16];
       char tmp[80];
       char *title[3] = {"Lon", "Lat", "Zoom"};
       DWORD id;

       switch (msg) {
               case WM_CREATE:
                       v = (VARS *)(((LPCREATESTRUCT)lParam)->lpCreateParams);
                       SetWindowLong(hwnd, 0, (LONG)v);

                       v->hwndStatus = CreateStatusWnd(v->hInst, hwnd, 2);
                       GetWindowRect(v->hwndStatus, &rect);
                       v->statusHeight = rect.bottom - rect.top;

                       hfnt = GetStockObject(ANSI_VAR_FONT);

                       for (i = 0; i < 3; i++) {
                               v->hwndLabel[i] = CreateWindowEx(
                                       0L, "STATIC", title[i],
                                       WS_CHILD | WS_VISIBLE | SS_RIGHT,
                                       0, 0, 0, 0,
                                       hwnd, NULL, v->hInst, NULL);

                               SendMessage(v->hwndLabel[i], WM_SETFONT, (WPARAM)hfnt,
                                       MAKELPARAM(1, 0));

                               v->hwndEdit[i] = CreateWindowEx(
                                       WS_EX_CLIENTEDGE, "EDIT", (LPCTSTR)NULL,
                                       WS_CHILD | WS_VISIBLE | ES_AUTOHSCROLL,
                                       0, 0, 0, 0,
                                       hwnd, NULL, v->hInst, NULL);

                               oldProc = (WNDPROC)GetWindowLong(v->hwndEdit[i], GWL_WNDPROC);
                               SetWindowLong(v->hwndEdit[i], GWL_WNDPROC, (LONG)EditProc);
                               SetWindowLong(v->hwndEdit[i], GWL_USERDATA, (LONG)oldProc);

                               SendMessage(v->hwndEdit[i], WM_SETFONT, (WPARAM)hfnt,
                                       MAKELPARAM(1, 0));
                       }

                       sprintf(tmp, "%.1f", v->origLon);
                       SetWindowText(v->hwndEdit[0], tmp);
                       sprintf(tmp, "%.1f", v->origLat);
                       SetWindowText(v->hwndEdit[1], tmp);
                       sprintf(tmp, "%.1f", v->zoom);
                       SetWindowText(v->hwndEdit[2], tmp);

                       SetFocus(v->hwndEdit[0]);

                       FileInitialize(hwnd);
                       return 0;

               case WM_SIZE:
                       SendMessage(v->hwndStatus, WM_SIZE, wParam, lParam);
                       SetStatusParts(v->hwndStatus, 2);

                       GetClientRect(hwnd, &rect);

                       for (i = 0; i < 3; i++) {
                               MoveWindow(v->hwndLabel[i],
                                               offs, rect.bottom - v->statusHeight - 32 + 3,
                                               40, 24, TRUE);

                               MoveWindow(v->hwndEdit[i],
                                               offs + 50, rect.bottom - v->statusHeight - 32,
                                               50, 24, TRUE);
                               offs += 130;
                       }

                       return 0;

               case WM_COMMAND:
                       id = LOWORD(wParam);

                       switch (id) {
                               case IDM_OPEN_WDB:
                                       AddWDBIIMap(v);
                                       return 0;

                               case IDM_EXIT:
                                       PostQuitMessage(0);
                                       return 0;

                               case IDM_RESET:
                                       if (! v->wdbIIMaps)
                                               return 0;
                                       DeleteWDBIIMaps(v);
                                       InvalidateRect(v->hwndMain, NULL, FALSE);
                                       UpdateWindow(v->hwndMain);
                                       return 0;

                               case IDM_SHW_COAST:
                               case IDM_SHW_COUNTRY:
                               case IDM_SHW_STATE:
                               case IDM_SHW_ISLAND:
                               case IDM_SHW_LAKE:
                               case IDM_SHW_RIVER:
                                       if (GetMenuState(v->hMenu, id, MF_BYCOMMAND) & MF_CHECKED) {
                                               CheckMenuItem(v->hMenu, id, MF_UNCHECKED);
                                               v->shwFeatures &= ~(id - 200);
                                       } else {
                                               CheckMenuItem(v->hMenu, id, MF_CHECKED);
                                               v->shwFeatures |= (id - 200);
                                       }

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

                               case IDM_CLR_BKGRND:
                               case IDM_CLR_COAST:
                               case IDM_CLR_COUNTRY:
                               case IDM_CLR_STATE:
                               case IDM_CLR_ISLAND:
                               case IDM_CLR_LAKE:
                               case IDM_CLR_RIVER:
                                       cc.lStructSize    = sizeof (CHOOSECOLOR);
                                       cc.hwndOwner      = hwnd;
                                       cc.hInstance      = (HWND)v->hInst;
                                       cc.rgbResult      = v->colors[id - 300];
                                       cc.lpCustColors   = crCustColors;
                                       cc.Flags          = CC_RGBINIT | CC_FULLOPEN;
                                       cc.lCustData      = 0;
                                       cc.lpfnHook       = NULL;
                                       cc.lpTemplateName = NULL;
                                       if (ChooseColor(&cc)) {
                                               v->colors[id - 300] = cc.rgbResult;
                                               InvalidateRect(v->hwndMain, NULL, FALSE);
                                               UpdateWindow(v->hwndMain);
                                       }
                                       return 0;

                               case IDM_CLR_RESET:
                                       memcpy(v->colors, v->defColors, sizeof(v->defColors));
                                       InvalidateRect(v->hwndMain, NULL, FALSE);
                                       UpdateWindow(v->hwndMain);
                                       return 0;

                               default:
                                       break;
                       }
                       break;

               case WM_PAINT:
                       for (i = 0; i < 3; i++) {
                               InvalidateRect(v->hwndLabel[i], NULL, TRUE);
                               UpdateWindow(v->hwndLabel[i]);
                               InvalidateRect(v->hwndEdit[i], NULL, TRUE);
                               UpdateWindow(v->hwndEdit[i]);
                       }
                       hdc = BeginPaint(hwnd, &ps);
                       DrawMap2(v, hdc);
                       EndPaint(hwnd, &ps);
                       if (! r)
                               exit(1);
                       return 0;

               case WM_ACTIVATE:
                       if (LOWORD(wParam) != WA_INACTIVE) {
                               if (v->oldFocus)
                                       SetFocus(v->oldFocus);
                       }
                       return 0;

               case WM_DESTROY:
                       SavePlacement(hwnd, "Software\\MapPlot");
                       SetRegVal("Software\\MapPlot", "Show", (void*)&(v->shwFeatures),
                                       sizeof(v->shwFeatures));
                       SetRegVal("Software\\MapPlot", "Colors", (void*)v->colors,
                                       sizeof(v->colors));
                       SetRegVal("Software\\MapPlot", "OrigLon", (void*)&(v->origLon),
                                       sizeof(v->origLon));
                       SetRegVal("Software\\MapPlot", "OrigLat", (void*)&(v->origLat),
                                       sizeof(v->origLat));
                       SetRegVal("Software\\MapPlot", "Zoom", (void*)&(v->zoom),
                                       sizeof(v->zoom));
                       PostQuitMessage(0);
                       return 0;
       }

       return DefWindowProc(hwnd, msg, wParam, lParam);
}


BOOL Init(HINSTANCE hInst, int nCmdShow)
{
       WNDCLASS wc;
       RECT rect;
       int x = CW_USEDEFAULT, y = 0, w = 640, h = 480;
       VARS *v;

       v = (VARS *)malloc(sizeof(VARS));
       v->hInst = hInst;
       v->oldFocus = NULL;
       v->mapData = v->prevPoly = NULL;
       v->wdbIIMode = FALSE;
       v->wdbIIMaps = NULL;

       v->defColors[0] = RGB(255,255,191);
       v->defColors[1] = RGB(32,144,32);
       v->defColors[2] = RGB(255,128,64);
       v->defColors[3] = RGB(255,0,0);
       v->defColors[4] = RGB(32,144,32);
       v->defColors[5] = RGB(128,128,255);
       v->defColors[6] = RGB(128,128,255);

       if (! ReadMaps(v))
               return FALSE;

       if (! GetRegVal("Software\\MapPlot", "Show", (void*)&(v->shwFeatures),
                       sizeof(v->shwFeatures))) {
               v->shwFeatures = SHW_COAST | SHW_ISLAND;
       }

       if (! GetRegVal("Software\\MapPlot", "Colors", (void*)v->colors,
                       sizeof(v->colors))) {
               memcpy(v->colors, v->defColors, sizeof(v->defColors));
       }

       if (! GetRegVal("Software\\MapPlot", "OrigLon", (void*)&(v->origLon),
                       sizeof(v->origLon))) {
               v->origLon = 0.0;
       }

       if (! GetRegVal("Software\\MapPlot", "OrigLat", (void*)&(v->origLat),
                       sizeof(v->origLat))) {
               v->origLat = 0.0;
       }

       if (! GetRegVal("Software\\MapPlot", "Zoom", (void*)&(v->zoom),
                       sizeof(v->zoom))) {
               v->zoom = 1.0;
       }

       wc.style = CS_HREDRAW | CS_VREDRAW;
       wc.lpfnWndProc = (WNDPROC)MainWndProc;
       wc.cbClsExtra = 0;
       wc.cbWndExtra = sizeof(v);
       wc.hInstance = hInst;
       wc.hIcon = LoadIcon(hInst, "Icon");
       wc.hCursor = LoadCursor(NULL, IDC_ARROW);
       wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
       wc.lpszMenuName = "MainMenu";
       wc.lpszClassName = "MainWndClass";
       if (! RegisterClass(&wc))
               return FALSE;

       if (GetPlacement("Software\\MapPlot", &rect)) {
               x = rect.left;
               y = rect.top;
               w = rect.right - rect.left;
               h = rect.bottom - rect.top;
       }

       v->hwndMain = CreateWindow(
               "MainWndClass",
               "MapPlot",
               WS_OVERLAPPEDWINDOW,
               x, y, w, h,
               NULL, NULL, v->hInst, (LPVOID)v);

       v->hMenu = GetMenu(v->hwndMain);

       if (v->shwFeatures & SHW_COAST)
               CheckMenuItem(v->hMenu, IDM_SHW_COAST, MF_CHECKED);
       if (v->shwFeatures & SHW_COUNTRY)
               CheckMenuItem(v->hMenu, IDM_SHW_COUNTRY, MF_CHECKED);
       if (v->shwFeatures & SHW_STATE)
               CheckMenuItem(v->hMenu, IDM_SHW_STATE, MF_CHECKED);
       if (v->shwFeatures & SHW_ISLAND)
               CheckMenuItem(v->hMenu, IDM_SHW_ISLAND, MF_CHECKED);
       if (v->shwFeatures & SHW_LAKE)
               CheckMenuItem(v->hMenu, IDM_SHW_LAKE, MF_CHECKED);
       if (v->shwFeatures & SHW_RIVER)
               CheckMenuItem(v->hMenu, IDM_SHW_RIVER, MF_CHECKED);

       ShowWindow(v->hwndMain, nCmdShow);
       UpdateWindow(v->hwndMain);
       return TRUE;
}


int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
               LPSTR lpCmdLine, INT nCmdShow)
{
       MSG msg;

       if (! Init(hInstance, nCmdShow))
               return 0;

       while (GetMessage (&msg, NULL, 0, 0)) {
               TranslateMessage(&msg);
               DispatchMessage(&msg);
       }

       return msg.wParam;
}