/*
 * (C) 2001-2004 Diomidis Spinellis
 * This file is part of GTWeb.
 *
 * GTWeb is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2, or (at your option) any later
 * version.
 *
 * GTWeb is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with groff; see the file COPYING.  If not, write to the Free Software
 * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 *
 * Ammend track log data with information about the nearest geographical name
 * The following fields are added:
 * Feature designation http://164.214.2.59/gns/html/fd_cross_ref.html
 * Administrative division http://164.214.2.59/gns/html/fips/fip10-4.html
 * Distance
 * Feature latitude
 * Feature longitude
 * Name
 *
 * Data should come from http://164.214.2.59/gns/html/cntry_files.html
 *
 * $Id: near.c 1.4 2004/02/07 08:35:19 dds Exp $
 *
 */

#include <stdio.h>
#include <math.h>
#include <string.h>
#include <assert.h>

struct point {
	float lat;
	float lon;
	enum {string, fileoffset} type;
	union {
		long offset;		// Offset into data file
		char *name;		// Saved name
	} u;
};

static void
skiptotab(FILE *f)
{
	int c;

	while ((c = getc(f)) != '\t')
		;
}

static void
printtotab(FILE *f)
{
	int c;

	while ((c = getc(f)) != '\t')
		putchar(c);
}

static void
uscoord(char *s, float *lat, float *lon)
{
	char coord[20];
	char *d;

	s += 80;
	d = coord + 1;
	*d++ = *s++;
	*d++ = *s++;
	*d++ = '.';
	*d++ = *s++;
	*d++ = *s++;
	*d++ = *s++;
	*d++ = *s++;
	*d = 0;
	coord[0] = (*s == 'N' ? ' ' : '-');
	if (sscanf(coord, "%g", lat) != 1)
		*lat = 0.0;
	s += 2;
	d = coord + 1;
	*d++ = *s++;
	*d++ = *s++;
	*d++ = *s++;
	*d++ = '.';
	*d++ = *s++;
	*d++ = *s++;
	*d++ = *s++;
	*d++ = *s++;
	*d = 0;
	coord[0] = (*s == 'W' ? '-' : ' ');
	if (sscanf(coord, "%g", lon) != 1)
		*lon = 0.0;
}

/*
 * Print feature type and county name and fill in the feature name
 */
static void
us_scan(FILE *fp, char *name, int namelen)
{
	char buff[512];
	int i;

	fgets(buff, sizeof(buff), fp);

	// Feature type
	for (i = 62; i > 55; i--)
		if (buff[i - 1] != ' ')
			break;
	buff[i] = 0;
	fputs(buff + 54, stdout);
	putchar('\t');

	// County
	for (i = 78; i > 65; i--)
		if (buff[i - 1] != ' ')
			break;
	buff[i] = 0;
	fputs(buff + 64, stdout);
	putchar('\t');

	// Name
	for (i = 52; i > 4; i--)
		if (buff[i - 1] != ' ')
			break;
	buff[i] = 0;
	strcpy(name, buff + 3);
}

static void
world_scan(FILE *fp, char *name, int namelen)
{
	skiptotab(fp); // C
	skiptotab(fp); // UFI
	skiptotab(fp); // DLAT
	skiptotab(fp); // DLONG
	skiptotab(fp); // LAT
	skiptotab(fp); // LONG
	skiptotab(fp); // MGRS
	skiptotab(fp); // UTM
	skiptotab(fp); // JOG
	skiptotab(fp); // FC
	skiptotab(fp); // DSG
	printtotab(fp); // PC
	putchar('\t');
	skiptotab(fp); // CC1
	printtotab(fp); // ADM1
	printtotab(fp); // ADM2
	putchar('\t');
	skiptotab(fp); // DIM
	skiptotab(fp); // CC2
	skiptotab(fp); // NT
	skiptotab(fp); // LC
	skiptotab(fp); // SHORT_FORM
	skiptotab(fp); // GENERIC
	skiptotab(fp); // SORT_NAME	
	skiptotab(fp); // FULL_NAME
	skiptotab(fp);
	fgets(name, namelen, fp);
	*strrchr(name, '\n') = 0;
}

main(int argc, char *argv[])
{
	FILE *fp;
	FILE *ft;
	char buff[1024];
	int n;
	int i, j;
	char ew, ns;
	int latdeg, londeg;
	float latmin, lonmin; 
	float lat, lon;
	float best, nbest;
	int besti;
	char feature[20];
	char country[20];
	char area[20];
	char name[512];
	struct point *p;
	int palloc;
	enum {f_us = 'u', f_world = 'w'} fmt;

	if (argc < 4 || *argv[1] != '-' || (argv[1][1] != 'u' && argv[1][1] != 'w')) {
		fprintf(stderr, "usage: %s -u|-w name-data [waypoints ... ] track-log ...\n", argv[0]);
		exit(1);
	}

	fmt = argv[1][1];
	/*
	 * The US data is delimited with a \n without a \r
	 * Visual C 5 generates wrong offsets with ftell when opening as "r"
	 */
	// Read feature list
	if ((fp = fopen(argv[2], fmt == f_world ? "r" : "rb")) == NULL) {
		perror(argv[2]);
		exit(1);
	}
	p = (struct point *)malloc((palloc = 50000) * sizeof(struct point));
	for (n = 0; p[n].u.offset = ftell(fp), fgets(buff, sizeof(buff), fp); n++) {
		if (n == palloc)
			p = (struct point *)realloc(p, (palloc *= 2) * sizeof(struct point));
		switch (fmt) {
		case f_world:
			sscanf(buff, "%*d %*d %*d %g %g", &p[n].lat, &p[n].lon);
			break;
		case f_us:
			uscoord(buff, &p[n].lat, &p[n].lon);
			break;
		default:	
			assert(0);
		}
		p[n].type = fileoffset;
		//printf("o=%d lat=%g lon=%g\n", p[n].offset, p[n].lat, p[n].lon);
	}

	for (i = 3; i < argc; i++) {
		if ((ft = fopen(argv[i], "r")) == NULL) {
			perror(argv[i]);
			exit(1);
		}
		while (fgets(buff, sizeof(buff), ft)) {
			if ((*buff != 'T' && *buff != 'W') || buff[1] != ' ')
				continue;
			switch (*buff) {
			case 'T':
				// T  N38 04.8777 E023 49.9539 Sun Aug 19 08:48:55 2001
				sscanf(buff, "%*c %c%d %g %c%d %g",
					&ns, &latdeg, &latmin, &ew, &londeg, &lonmin);
				break;
			case 'W':
				// W  YPATI  N38 52.1999 E022 14.4999 Sun Dec 31 00:00:00 1989 09-NOV-01 22:45 Ypati
				sscanf(buff + 10, "%c%d %g %c%d %g",
					&ns, &latdeg, &latmin, &ew, &londeg, &lonmin);
				break;
			default:
				assert(0);
			}
			//fputs(buff, stdout);
			//printf("%c %d %g|%c %d %g\n", ns, latdeg, latmin, ew, londeg, lonmin);
			lat = latdeg + latmin / 60;
			if (ns == 'S')
				lat = -lat;
			lon = londeg + lonmin / 60;
			if (ew == 'W')
				lon = -lon;
			if (*buff == 'W') {
				// Waypoint; store it for future reference
				if (n == palloc)
					p = (struct point *)realloc(p, (palloc *= 2) * sizeof(struct point));
				p[n].lat = lat;
				p[n].lon = lon;
				p[n].type = string;
				if (buff[76] != ' ') {
					// Remove trailing spaces
					for (j = strlen(buff) - 2; j > 0; j++)
						if (buff[j] != ' ') {
							buff[j + 1] = 0;
							break;
						}
					p[n].u.name = strdup(buff + 76);
				} else {
					buff[9] = 0;
					p[n].u.name = strdup(buff + 3);
				}
				// printf("n=%s lat=%g lon=%g\n", p[n].u.name, p[n].lat, p[n].lon);
				n++;
				continue;
			}
			// Track point; use it
			// Linear search :-(
			best = 1e38;
			for (j = 0; j < n; j++) {
				if ((nbest = hypot(lat - p[j].lat, lon - p[j].lon)) < best) {
					best = nbest;
					besti = j;
				}
				//printf("best: %g %g %g %g %g\n", nbest, p[j].lat, p[j].lon, lat, lon);
			}
			*strrchr(buff, '\n') = 0;
			printf("%s ", buff);
			switch (p[besti].type) {
			case fileoffset:
				fseek(fp, p[besti].u.offset, SEEK_SET);
				//printf("o=%d\n", p[besti].offset);
				switch (fmt) {
				case f_us:
					us_scan(fp, name, sizeof(name));
					break;
				case f_world:
					world_scan(fp, name, sizeof(name));
					break;
				}
				break;
			case string:
				printf(" \t \t");
				strcpy(name, p[besti].u.name);
				break;
			default:	
				assert(0);
			}
			printf(" %g %g %g %s\n", best, p[besti].lat, p[besti].lon, name);
		}
		fclose(ft);
	}
	exit(0);
}
