/**
 * Command line program to parse the world airports database as provided by:
 * http://ourairports.com
 * 
 * Data downloaded 2017-11-01. The following files are required by this
 * program and must be stored in a single directory whose path must be indicated
 * on the command line: airports.csv, navaids.csv, runways.csv.
 * 
 * Writes to standard output the generated scenery file with RWY and NAV records
 * only. For a detailed description of the command line options, use --help.
 * 
 * Unfortunately, ILS antennas are missing from that database, so only RWY and
 * NAV records are generated.
 * 
 * The only purpose of the airports file is to get the average elevation of its
 * runways, as the ACM does not support (or, it behaves badly) close runways at
 * different elevation (see the reference manual for more about this issue).
 * 
 * @file
 * @author Umberto Salsi <salsi@icosaedro.it>
 * @version $Date: 2017/11/16 17:05:25 $
 */

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

// LINKER_OPTIONS -lm

#include "../../src/util/error.h"
#include "../../src/util/hashtable.h"
#include "../../src/util/memory.h"
#include "../../src/util/reader.h"
#include "../../src/util/units.h"
#include "../../src/dis/dis/earth.h"

#define MAX_FIELDS 99


/*
 * Range of latitude and longitude of the items we are interested to extract.
 * The value must be greater or equal to the minimum, and strictly less than the
 * maximum.
 * For runways, the base end is compared.
 * For ILS, the position of the localized antenna is compared.
 * For NDB and VOR with associated DME, the location of the NDB or VOR antenna
 * is compared.
 */
static double lat_min = units_DEGtoRAD(-90);
static double lat_max = units_DEGtoRAD(91);
static double lon_min = units_DEGtoRAD(-180);
static double lon_max = units_DEGtoRAD(181);


static long parseLong(char *s, long *l)
{
	char *tail;
	*l = strtol(s, &tail, 10);
	return tail != s && *tail == 0;
}


static int parseDouble(char *s, double *d)
{
	char *tail;
	*d = strtod(s, &tail);
	return tail != s && *tail == 0 && isfinite(*d);
}


static int foundExpectdHeaderFields(reader_Type *in, char **exp_fields, int got_fields_number, char **got_fields)
{
	int err = 0;
	int i = 0;
	while(exp_fields[i] != NULL){
		if( i >= got_fields_number ){
			fprintf(stderr, "%s:%d: missing expected fields: ", reader_getPath(in), reader_getLineNumber(in));
			do {
				fprintf(stderr, " %d)%s", i+1, exp_fields[i]);
				i++;
			} while(exp_fields[i] != NULL);
			fprintf(stderr, "\n");
			return 0;
		}
		if( strcmp(exp_fields[i], got_fields[i]) != 0 ){
			fprintf(stderr, "%s:%d: expected field no. %d \"%s\" but got \"%s\"\n", reader_getPath(in), reader_getLineNumber(in), i+1, exp_fields[i], got_fields[i]);
			err++;
		}
		i++;
	}
	if( i < got_fields_number ){
		fprintf(stderr, "%s:%d: unexpected further fields: ", reader_getPath(in), reader_getLineNumber(in));
		do {
			fprintf(stderr, " %d)%s", i+1, got_fields[i]);
			i++;
		} while(i < got_fields_number);
		fprintf(stderr, "\n");
	}
	return err == 0;
}


typedef struct {
	char *pk; // airport primary key of the data base
	int line_no;  // line no. in the airports file (for duplicates error reporting)
	int elevation; // average elevation assumed for all the runways of this airport (ft)
	int isAvailable; // if open, operational, fixed-wings airport
} Airport;


/**
 * Maps airport's pk to the allocated Airport.
 */
static hashtable_Type *airports;


static int parseAirports(char *path)
{
	airports = hashtable_new(hashtable_getHashOfString, hashtable_equalStrings);
	reader_Type *in = reader_new(path);
	char line[999];
	while( reader_getLine(in, line, sizeof(line)) ){
		int argc;
		char *argv[MAX_FIELDS];
		if( ! reader_splitDoubleQuotedCommaSeparated(line, &argc, argv, MAX_FIELDS) ){
			fprintf(stderr, "%s:%d: more than %d fields found\n",
				reader_getPath(in), reader_getLineNumber(in), MAX_FIELDS);
			return 0;
		}
		if( reader_getLineNumber(in) == 1 ){
			if( ! foundExpectdHeaderFields(in,
				(char *[]){
/*00*/ "id",
/*01*/ "ident",
/*02*/ "type",
/*03*/ "name",
/*04*/ "latitude_deg",
/*05*/ "longitude_deg",
/*06*/ "elevation_ft",
/*07*/ "continent",
/*08*/ "iso_country",
/*09*/ "iso_region",
/*10*/ "municipality",
/*11*/ "scheduled_service",
/*12*/ "gps_code",
/*13*/ "iata_code",
/*14*/ "local_code",
/*15*/ "home_link",
/*16*/ "wikipedia_link",
/*17*/ "keywords", NULL
}, argc, argv) )
				return 1;
		}
		
		// At
		if( argc < 7 ){
			fprintf(stderr, "%s:%d: less than 7 fields in this record, so no elevation fro this airport\n", reader_getPath(in),
				reader_getLineNumber(in));
		}
		
		// Empty id are silently ignored:
		char *id = argv[0];
		if( strlen(id) == 0 )
			continue;
		
		// Invalid elevation values assumed zero:
		double elevation;
		if( ! parseDouble(argv[6], &elevation) )
			elevation = 0.0;
		// Deeper known airfield in this DB is the Bar Yehuda Airfield 31.33N 35.39E
		// at -1266 ft under the WGS-84 ellipsoid (roughly, under the sea level)!
		// So, -1300 seems a safe lower limit to check for; +24000 definitely too hight!
		if( !(-1300 <= elevation && elevation <= 24000) ){
			fprintf(stderr, "%s:%d: airport elevation out of range: %g ft -- ignoring\n",
				reader_getPath(in), reader_getLineNumber(in), elevation);
			continue;
		}
		
		Airport *airport = hashtable_get(airports, id);
		if( airport != NULL ){
			fprintf(stderr, "%s:%d: duplicated airport pk %s in line %d\n", reader_getPath(in),
				reader_getLineNumber(in), id, airport->line_no);
		}
		
		// Check if type of airport is operational, fixed-wings;
		int isAvailable = 1;
		if( strcmp(argv[2], "closed") == 0
		|| strcmp(argv[2], "heliport") == 0
		|| strcmp(argv[2], "balloonport") == 0
		){
			isAvailable = 0;
		} else if( strcmp(argv[2], "small_airport") == 0
		|| strcmp(argv[2], "medium_airport") == 0
		|| strcmp(argv[2], "large_airport") == 0
		|| strcmp(argv[2], "seaplane_base") == 0
		){
			isAvailable = 1;
		} else {
			isAvailable = 0;
			fprintf(stderr, "%s:%d: unknown airport type: %s -- ignoring\n",
				reader_getPath(in), reader_getLineNumber(in), argv[2]);
		}
		
		airport = memory_allocate(sizeof(Airport), NULL);
		airport->pk = memory_strdup(id);
		airport->line_no = reader_getLineNumber(in);
		airport->elevation = (int) round(elevation);
		airport->isAvailable = isAvailable;
		hashtable_put(airports, airport->pk, airport);
		
/*
		if( reader_getLineNumber(in) <= 30 ){
			printf("Line no. %d:\n", reader_getLineNumber(in));
			int i;
			for(i = 0; i < argc; i++){
				printf(" %d)%s", i, argv[i]);
			}
			printf("\n");
		}
*/
	}
	if( reader_getError(in) != NULL ){
		fprintf(stderr, "%s:%d: %s\n", reader_getPath(in),
			reader_getLineNumber(in), reader_getError(in));
		return 0;
	}
	memory_dispose(in);

	return 1;
}


static int parseRunways(char *path)
{
	reader_Type *in = reader_new(path);
	char line[999];
	while( reader_getLine(in, line, sizeof(line)) ){
		int argc;
		char *argv[MAX_FIELDS];
		if( ! reader_splitDoubleQuotedCommaSeparated(line, &argc, argv, MAX_FIELDS) ){
			fprintf(stderr, "%s:%d: more than %d fields found\n",
				reader_getPath(in), reader_getLineNumber(in), MAX_FIELDS);
			return 0;
		}
		if( reader_getLineNumber(in) == 1 ){
			if( ! foundExpectdHeaderFields(in,
				(char *[]){
				/*00*/ "id",
				/*01*/ "airport_ref",
				/*02*/ "airport_ident",
				/*03*/ "length_ft",
				/*04*/ "width_ft",
				/*05*/ "surface",
				/*06*/ "lighted",
				/*07*/ "closed",
				/*08*/ "le_ident",
				/*09*/ "le_latitude_deg",
				/*10*/ "le_longitude_deg",
				/*11*/ "le_elevation_ft",
				/*12*/ "le_heading_degT",
				/*13*/ "le_displaced_threshold_ft",
				/*14*/ "he_ident",
				/*15*/ "he_latitude_deg",
				/*16*/ "he_longitude_deg",
				/*17*/ "he_elevation_ft",
				/*18*/ "he_heading_degT",
				/*19*/ "he_displaced_threshold_ft",
				NULL
}, argc, argv) )
				return 0;
		} else {
			if( argc < 17 ){
				fprintf(stderr, "%s:%d: missing fields -- ignoring\n",
					reader_getPath(in), reader_getLineNumber(in));
				continue;
			}
			
			
			// Most lat/lon are missing, we may safely silently ignore.
			if( argv[9][0] == 0 || argv[10][0] == 0
			|| argv[15][0] == 0 || argv[16][0] == 0 )
				continue;
			
			double end1_lat;
			if( ! parseDouble(argv[9], &end1_lat) || !(-90 <= end1_lat && end1_lat <= 90) ){
				fprintf(stderr, "%s:%d: invalid runway latitude: %s -- ignoring\n",
					reader_getPath(in), reader_getLineNumber(in), argv[9]);
				continue;
			}
			double end1_lon;
			if( ! parseDouble(argv[10], &end1_lon) || !(-180 <= end1_lon && end1_lon <= 180) ){
				fprintf(stderr, "%s:%d: invalid runway longitude: %s -- ignoring\n",
					reader_getPath(in), reader_getLineNumber(in), argv[10]);
				continue;
			}
			double end2_lat;
			if( ! parseDouble(argv[15], &end2_lat) || !(-90 <= end2_lat && end2_lat <= 90) ){
				fprintf(stderr, "%s:%d: invalid runway latitude: %s -- ignoring\n",
					reader_getPath(in), reader_getLineNumber(in), argv[15]);
				continue;
			}
			double end2_lon;
			if( ! parseDouble(argv[16], &end2_lon) || !(-180 <= end2_lon && end2_lon <= 180) ){
				fprintf(stderr, "%s:%d: invalid runway longitude: %s -- ignoring\n",
					reader_getPath(in), reader_getLineNumber(in), argv[16]);
				continue;
			}
			
			end1_lat = units_DEGtoRAD(end1_lat);
			end1_lon = units_DEGtoRAD(end1_lon);
			end2_lat = units_DEGtoRAD(end2_lat);
			end2_lon = units_DEGtoRAD(end2_lon);
			
			// Detects N/S and W/E mismatches (there are some):
			if( fabs(end1_lat - end2_lat) > 1 ){
				fprintf(stderr, "%s:%d: runway ends latitudes too far -- ignoring\n",
					reader_getPath(in), reader_getLineNumber(in));
				continue;
			}
			if( fabs(end1_lon - end2_lon) * cos(end1_lat) > 1 ){
				fprintf(stderr, "%s:%d: runway ends longitudes too far -- ignoring\n",
					reader_getPath(in), reader_getLineNumber(in));
				continue;
			}
		
			// Ignore runway if middle point lies outside the range:
			double center_lat = (end1_lat + end2_lat)/2;
			double center_lon = (end1_lon + end2_lon)/2;
			if( !(lat_min <= center_lat && center_lat < lat_max
			&& lon_min <= center_lon && center_lon < lon_max) )
				continue;
			
			char *surface = argv[5];
			// ACCEPT THESE SURFACES:
			if( memory_startsWith(surface, "CONC")
			|| memory_startsWith(surface, "CON")
			|| memory_startsWith(surface, "ASP")
			|| memory_startsWith(surface, "Asphalt")
			|| strcmp(surface, "asphalt") == 0
			|| strcmp(surface, "concrete") == 0
			|| strcmp(surface, "BIT") == 0  // bitumen?
			|| strcmp(surface, "GRE") == 0  // ?
			|| strcmp(surface, "GRS") == 0  // ?
			|| strcmp(surface, "PEM") == 0  // ?
			){
			
			// SILENTLY REJECT THESE SURFACES:
			} else if( memory_startsWith(surface, "WATER")
			|| memory_startsWith(surface, "TURF")
			|| memory_startsWith(surface, "Turf")
			|| memory_startsWith(surface, "DIRT")
			|| strcmp(surface, "GRASS") == 0
			|| memory_startsWith(surface, "Grass")
			|| strcmp(surface, "Gravel") == 0
			|| memory_startsWith(surface, "GRAVEL")
			|| memory_startsWith(surface, "GRVL")
			|| memory_startsWith(surface, "GVL")
			|| strcmp(surface, "MATS") == 0
			){
				continue;
			
			// ERROR ON ANY OTHER UNEXPECTED SURFACE TYPE:
			} else {
				fprintf(stderr, "%s:%d: unknown runway surface: %s -- ignoring\n",
					reader_getPath(in), reader_getLineNumber(in), surface);
				continue;
			}
			
			char *airport_ident = argv[2];
			if( strlen(airport_ident) < 3 ){
				fprintf(stderr, "%s:%d: airport code too short: %s -- ignoring\n",
					reader_getPath(in), reader_getLineNumber(in), airport_ident);
				continue;
			}
			if( strlen(airport_ident) > 4 ){
				fprintf(stderr, "%s:%d: airport code too long: %s -- ignoring\n",
					reader_getPath(in), reader_getLineNumber(in), airport_ident);
				continue;
			}
			char *end1_id = argv[8];
			if( strlen(end1_id) < 2 || strlen(end1_id) > 3 ){
				fprintf(stderr, "%s:%d: invalid runway ID length: %s -- ignoring\n",
					reader_getPath(in), reader_getLineNumber(in), end1_id);
				continue;
			}
			char *end2_id = argv[14];
			if( strlen(end2_id) < 2 || strlen(end2_id) > 3 ){
				fprintf(stderr, "%s:%d: invalid runway ID length: %s -- ignoring\n",
					reader_getPath(in), reader_getLineNumber(in), end2_id);
				continue;
			}
			char stripe[8];
			snprintf(stripe, sizeof(stripe), "%s/%s", end1_id, end2_id);
			
			int elevation = 0;
			char *airport_pk = argv[1];
			Airport *airport = hashtable_get(airports, airport_pk);
			if( airport == NULL ){
				fprintf(stderr, "%s:%d: airport pk %s not found in the airports file\n",
					reader_getPath(in), reader_getLineNumber(in), airport_pk);
				continue;
			} else if( ! airport->isAvailable ){
				continue;
			} else {
				elevation = airport->elevation;
			}
			
			double length;
			if( ! parseDouble(argv[3], &length) ){
				fprintf(stderr, "%s:%d: invalid runway length: %s -- ignoring\n",
					reader_getPath(in), reader_getLineNumber(in), argv[3]);
				continue;
			}
			double width;
			if( ! parseDouble(argv[4], &width) ){
				fprintf(stderr, "%s:%d: invalid runway width: %s -- ignoring\n",
					reader_getPath(in), reader_getLineNumber(in), argv[4]);
				continue;
			}
			
			char lat1[99], lon1[99], lat2[99], lon2[99];
			earth_latitudeToString(lat1, sizeof(lat1), end1_lat, earth_LLM_DMS);
			earth_longitudeToString(lon1, sizeof(lon1), end1_lon, earth_LLM_DMS);
			earth_latitudeToString(lat2, sizeof(lat2), end2_lat, earth_LLM_DMS);
			earth_longitudeToString(lon2, sizeof(lon2), end2_lon, earth_LLM_DMS);
			printf("RWY %-4s %-7s %4d %5.0f %3.0f %s %s %s %s\n",
				airport_ident, stripe, elevation, length, width, lat1, lon1, lat2, lon2);
			
		}
/*
		if( reader_getLineNumber(in) <= 90 ){
			printf("Line no. %d:\n", reader_getLineNumber(in));
			int i;
			for(i = 0; i < argc; i++){
				printf("/ *%02d* / \"%s\"\n", i, argv[i]);
			}
		}
*/
	}
	if( reader_getError(in) != NULL ){
		fprintf(stderr, "%s:%d: %s\n", reader_getPath(in),
			reader_getLineNumber(in), reader_getError(in));
		return 0;
	}
	memory_dispose(in);

	return 1;
}


static int parseNavaids(char *path)
{
	reader_Type *in = reader_new(path);
	char line[999];
	while( reader_getLine(in, line, sizeof(line)) ){
		int argc;
		char *argv[MAX_FIELDS];
		if( ! reader_splitDoubleQuotedCommaSeparated(line, &argc, argv, MAX_FIELDS) ){
			fprintf(stderr, "%s:%d: more than %d fields found\n",
				reader_getPath(in), reader_getLineNumber(in), MAX_FIELDS);
			return 0;
		}
		if( reader_getLineNumber(in) == 1 ){
			if( ! foundExpectdHeaderFields(in,
				(char *[]){
					/*00*/ "id",
					/*01*/ "filename",
					/*02*/ "ident",
					/*03*/ "name",
					/*04*/ "type",
					/*05*/ "frequency_khz",
					/*06*/ "latitude_deg",
					/*07*/ "longitude_deg",
					/*08*/ "elevation_ft",
					/*09*/ "iso_country",
					/*10*/ "dme_frequency_khz",
					/*11*/ "dme_channel",
					/*12*/ "dme_latitude_deg",
					/*13*/ "dme_longitude_deg",
					/*14*/ "dme_elevation_ft",
					/*15*/ "slaved_variation_deg",
					/*16*/ "magnetic_variation_deg",
					/*17*/ "usageType",
					/*18*/ "power",
					/*19*/ "associated_airport",
				NULL
}, argc, argv) )
				return 0;
		} else {
			if( argc < 14 ){
				fprintf(stderr, "%s:%d: missing fields -- ignoring\n",
					reader_getPath(in), reader_getLineNumber(in));
				continue;
			}
			
			
			// Most lat/lon are missing, we may safely silently ignore.
			if( argv[6][0] == 0 || argv[7][0] == 0 )
				continue;
			
			double lat;
			if( ! parseDouble(argv[6], &lat) || !(-90 <= lat && lat <= 90) ){
				fprintf(stderr, "%s:%d: invalid latitude: %s -- ignoring\n",
					reader_getPath(in), reader_getLineNumber(in), argv[6]);
				continue;
			}
			double lon;
			if( ! parseDouble(argv[7], &lon) || !(-180 <= lon && lon <= 180) ){
				fprintf(stderr, "%s:%d: invalid longitude: %s -- ignoring\n",
					reader_getPath(in), reader_getLineNumber(in), argv[7]);
				continue;
			}
			
			lat = units_DEGtoRAD(lat);
			lon = units_DEGtoRAD(lon);
		
			if( !(lat_min <= lat && lat < lat_max
			&& lon_min <= lon && lon < lon_max) )
				continue;
			
			int mhz = 0; // 0=KHz, 1=MHz
			char *type = argv[4];
			
			// ACCEPT THESE TYPES:
			if( strcmp(type, "VORTAC") == 0
			|| strcmp(type, "TACAN") == 0
			|| strcmp(type, "DME") == 0
			|| strcmp(type, "VOR") == 0
			){
				mhz = 1;
			
			} else if( strcmp(type, "NDB") == 0 ){
				mhz = 0;
			
			} else if( strcmp(type, "NDB-DME") == 0 ){
				// No support for NDB/DME under ACM, sorry.
				type = "NDB";
				mhz = 0;
			
			} else if( strcmp(type, "NDB-DME") == 0 ){
				// No support for NDB/DME under ACM, sorry.
				type = "NDB";
				mhz = 0;
			
			} else if( strcmp(type, "VOR-DME") == 0 ){
				type = "VOR/DME";
				mhz = 1;
			
			// SILENTLY REJECT THESE TYPES:
			} else if( memory_startsWith(type, "xxxxx")
			){
				continue;
			
			// ERROR ON ANY OTHER UNEXPECTED NAVAID TYPE:
			} else {
				fprintf(stderr, "%s:%d: unknown NAVAID type: %s -- ignoring\n",
					reader_getPath(in), reader_getLineNumber(in), type);
				continue;
			}
			
			char *name = argv[2];
			if( strlen(name) < 2 ){
				fprintf(stderr, "%s:%d: NAVAID name too short: %s -- ignoring\n",
					reader_getPath(in), reader_getLineNumber(in), name);
				continue;
			}
			if( strlen(name) > 4 ){
				fprintf(stderr, "%s:%d: NAVAID name too long: %s -- ignoring\n",
					reader_getPath(in), reader_getLineNumber(in), name);
				continue;
			}
			
			long freq_khz;
			if( ! parseLong(argv[5], &freq_khz) ){
				fprintf(stderr, "%s:%d: failed to parse NAVAID frequency as an integral number of KHz: %s -- ignoring\n",
					reader_getPath(in), reader_getLineNumber(in), argv[5]);
				continue;
			}
			char freq[9];
			if( mhz ){
				if( !(108000 <= freq_khz && freq_khz <= 117950) ){
					fprintf(stderr, "%s:%d: DME or VOR frequency out of the range [108000,117950] KHz: %ld -- ignoring\n",
						reader_getPath(in), reader_getLineNumber(in), freq_khz);
					continue;
				}
				if( (freq_khz % 50) != 0 ){
					fprintf(stderr, "%s:%d: invalid DME or VOR frequency, expected multiple of 50 KHz: %ld -- ignoring\n",
						reader_getPath(in), reader_getLineNumber(in), freq_khz);
					continue;
				}
				snprintf(freq, sizeof(freq), "%.2f", freq_khz / 1000.0);
				
			} else {
				if( !(200 <= freq_khz && freq_khz <= 529) ){
					fprintf(stderr, "%s:%d: NDB frequency out of the range [200,529] KHz: %ld -- ignoring\n",
						reader_getPath(in), reader_getLineNumber(in), freq_khz);
					continue;
				}
				snprintf(freq, sizeof(freq), "%ld", freq_khz);
			}
			
			double elevation;
			if( argv[8][0] == 0 ){
				// Most altitudes are blank. Silently set zero.
				elevation = 0;
			} else if( ! parseDouble(argv[8], &elevation) ){
				fprintf(stderr, "%s:%d: cannot parse NAVAID altitude: %s -- assuming zero\n",
					reader_getPath(in), reader_getLineNumber(in), argv[8]);
				elevation = 0;
			}
			
			char lat_s[99], lon_s[99];
			earth_latitudeToString(lat_s, sizeof(lat_s), lat, earth_LLM_DMS);
			earth_longitudeToString(lon_s, sizeof(lon_s), lon, earth_LLM_DMS);
			printf("NAV %-4s %-7s %s %s %4.0f %6s -\n",
				name, type, lat_s, lon_s, elevation, freq);
			
		}
/*
		if( reader_getLineNumber(in) <= 9 ){
			printf("Line no. %d:\n", reader_getLineNumber(in));
			int i;
			for(i = 0; i < argc; i++){
				printf(" %d)%s", i, argv[i]);
			}
			printf("\n");
		}
*/
	}
	if( reader_getError(in) != NULL ){
		fprintf(stderr, "%s:%d: %s\n", reader_getPath(in),
			reader_getLineNumber(in), reader_getError(in));
		return 0;
	}
	memory_dispose(in);

	return 1;
}


static void help()
{
	puts(
	"This program parses the ourairports.com data base in its textual format and generates\n"
	"an ACM scenery. The ourairports-com data base can be downloaded from\n"
	"https://ourairports.com\n"
	"Download the airports.csv, runways.csv and navaids.csv files in a folder,\n"
	"then start this program with the following command line parameters:\n"
	"\n"
	"   --lat-min DEG   Minimum latitude, default 90S.\n"
	"   --lat-max DEG   Maximum latitude, default 90N.\n"
	"   --lon-min DEG   Minimum longitude, default 180W.\n"
	"   --lon-max DEG   maximum longitude, default 180E.\n"
	"   --src-dir PATH  Path to the directory of the data base CSV files,\n"
	"                   default . (current directory).\n"
	"\n"
	"The program collects all the runways and NAVAID in the given range of\n"
	"latitude and longitude and writes the scenery on standard output; errors and\n"
	"warnings are sent to standard error. Only the RWY and NAV records\n"
	"are generated. You may want to add the TEAM1_LOC, TEAM2_LOC and GROUND_COLOR\n"
	"records to complete the scenery; ILS must be added by hand, sorry.\n"
	);
}


int main(int argc, char** argv)
{
	error_init(argv[0]);
	char *src_dir = ".";
	
	int i = 1;
	while( i < argc ){
		char *a = argv[i];
		char *v = i < argc? argv[i+1] : NULL;
		if( strcmp(a, "--help") == 0 || strcmp(a, "-h") == 0 ){
			help();
			return 0;
		} else if( strcmp(a, "--lat-min") == 0 && v != NULL ){
			if( ! earth_parseLatitude(v, &lat_min) )
				error_external("invalid latitude: %s", v);
			i += 2;
		} else if( strcmp(a, "--lat-max") == 0 && v != NULL ){
			if( ! earth_parseLatitude(v, &lat_max) )
				error_external("invalid latitude: %s", v);
			i += 2;
		} else if( strcmp(a, "--lon-min") == 0 && v != NULL ){
			if( ! earth_parseLongitude(v, &lon_min) )
				error_external("invalid longitude: %s", v);
			i += 2;
		} else if( strcmp(a, "--lon-max") == 0 && v != NULL ){
			if( ! earth_parseLongitude(v, &lon_max) )
				error_external("invalid longitude: %s", v);
			i += 2;
		} else if( strcmp(a, "--src-dir") == 0 && v != NULL ){
			src_dir = v;
			i += 2;
		} else {
			error_external("unknown option/value: %s", a);
		}
	}
	
	if( !(lat_min < lat_max && lon_min < lon_max) )
		error_external("invalid latitude [%g,%g[ DEG or longitude [%g,%g[ DEG ranges", units_RADtoDEG(lat_min), units_RADtoDEG(lat_max), units_RADtoDEG(lon_min), units_RADtoDEG(lon_max));
	
	char path[999];
	
	snprintf(path, sizeof(path), "%s/airports.csv", src_dir);
	if( ! parseAirports(path) )
		return 1;
	
	snprintf(path, sizeof(path), "%s/runways.csv", src_dir);
	if( ! parseRunways(path) )
		return 1;
	
	snprintf(path, sizeof(path), "%s/navaids.csv", src_dir);
	if( ! parseNavaids(path) )
		return 1;
	
	return 0;
}


