/* main-command.c
 * Written by Daniel Foote.
 * Started Feb 2005.
 *
 * Command line program to match GPS data and Photo EXIF timestamps
 * together, to figure out where you were at the time.
 * Writes the output back into the GPS exif tags.
 */

/* Copyright 2005-2019 Daniel Foote, Dan Fandrich.
 *
 * This file is part of gpscorrelate.
 *
 * gpscorrelate 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 of the License, or
 * (at your option) any later version.
 *
 * gpscorrelate 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 gpscorrelate; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <getopt.h>
#include <string.h>
#include <locale.h>
#include <math.h>

#include "i18n.h"
#include "gpsstructure.h"
#include "exif-gps.h"
#include "unixtime.h"
#include "gpx-read.h"
#include "latlong.h"
#include "correlate.h"

#define GPS_EXIT_WARNING 2

/* Command line options structure. */
static const struct option program_options[] = {
	{ "gps", required_argument, 0, 'g' },
	{ "latlong", required_argument, 0, 'l' },
	{ "timeadd", required_argument, 0, 'z'},
	{ "no-interpolation", no_argument, 0, 'i'},
	{ "help", no_argument, 0, 'h'},
	{ "verbose", no_argument, 0, 'v'},
	{ "datum", required_argument, 0, 'd'},
	{ "no-write", no_argument, 0, 'n'},
	{ "replace", no_argument, 0, 'R'},
	{ "max-dist", required_argument, 0, 'm'},
	{ "show", no_argument, 0, 's'},
	{ "machine", no_argument, 0, 'o'},
	{ "remove", no_argument, 0, 'r'},
	{ "ignore-tracksegs", no_argument, 0, 't'},
	{ "no-mtime", no_argument, 0, 'M'},
	{ "version", no_argument, 0, 'V'},
	{ "fix-datestamps", no_argument, 0, 'f'},
	{ "degmins", no_argument, 0, 'p'},
	{ "photooffset", required_argument, 0, 'O'},
	{ 0, 0, 0, 0 }
};

/* Function to print the version - near the top for easy modification. */
static void PrintVersion(const char* ProgramName)
{
	printf(_("%s, ver. %s. %s\n"),
	       ProgramName, PACKAGE_VERSION,
	       _("Copyright (C) 2005-2019 Daniel Foote, Dan Fandrich"));
}

/* Function to print the usage info. */
static void PrintUsage(const char* ProgramName)
{
	printf(_("Usage: %s [options] file.jpg ...\n"), ProgramName);
	puts(  _("-g, --gps file.gpx       Specifies GPX file with GPS data"));
	puts(  _("-l, --latlong LAT,LONG[,E] Specifies latitude/longitude/elevation directly"));
	puts(  _("-z, --timeadd +/-HH[:MM] Time to add to GPS data to make it match photos"));
	puts(  _("-i, --no-interpolation   Disable interpolation between points; interpolation\n"
	         "                         is linear, points rounded if disabled"));
	puts(  _("-d, --datum DATUM        Specify measurement datum (defaults to WGS-84)"));
	puts(  _("-n, --no-write           Do not write the EXIF data. Useful with --verbose"));
	puts(  _("-R, --replace            Overwrite any GPS tags already in the image file"));
	puts(  _("-m, --max-dist SECS      Max time outside points that photo will be matched"));
	puts(  _("-s, --show               Just show the GPS data from the given files"));
	puts(  _("-o, --machine            Similar to --show but with machine-readable output"));
	puts(  _("-r, --remove             Strip GPS tags from the given files"));
	puts(  _("-t, --ignore-tracksegs   Interpolate between track segments, too"));
	puts(  _("-M, --no-mtime           Don't change mtime of modified files"));
	puts(  _("-f, --fix-datestamps     Fix broken GPS datestamps written with ver. < 1.5.2"));
	puts(  _("    --degmins            Write location as DD MM.MM (was default before v1.5.3)"));
	puts(  _("-O, --photooffset SECS   Offset added to photo time to make it match the GPS"));
	puts(  _("-h, --help               Display this help message"));
	puts(  _("-v, --verbose            Show more detailed output"));
	puts(  _("-V, --version            Display version information"));
}

/* CSV escape a string by doubling quotation marks.
 * A pointer to a malloced string is returned which must be freed by the caller.
 */
static char *CsvEscape(const char *str)
{
	char *newstr = (char*)malloc(strlen(str)*2+1); // worst-case size
	char *cpy = newstr;
	if (!newstr)
		return newstr;
	do {
		*cpy = *str;
		if (*cpy++ == '"')
			*cpy++ = '"';
	} while (*str++);
	return newstr;
}

/* Display the information from an existing file. */
static int ShowFileDetails(const char* File, int MachineReadable)
{
	double Lat, Long, Elev;
	int IncludesGPS = 0;
	Lat = Long = 0;
	Elev = NAN; /* Elevation is optional, so this means it's missing */
	char* Time = ReadExifData(File, &Lat, &Long, &Elev, &IncludesGPS);
	int rc = 1;
	char* OldLocale = NULL;

	if (MachineReadable)
	{
		OldLocale = setlocale(LC_NUMERIC, "C");
	}

	if (Time)
	{
		if (IncludesGPS)
		{
			/* Display the data as CSV if we want
			 * it machine readable. */
			if (MachineReadable)
			{
				char *EscapedFile = CsvEscape(File);
				if (!EscapedFile) {
					fprintf(stderr, _("Out of memory.\n"));
					exit(EXIT_FAILURE);
				}
				printf("\"%s\",\"%s\",%f,%f,",
					EscapedFile, Time, Lat, Long);

				if (!isnan(Elev))
					printf("%.3f", Elev);
				printf("\n");
				free(EscapedFile);
			} else {
				printf(_("%s: %s, Lat %f, Long %f, Elevation "),
					File, Time, Lat, Long);
				if (!isnan(Elev))
					printf("%.3f", Elev);
                                else
					printf(_("(unknown)"));

				printf(".\n");
			}
		} else {
			/* Don't display anything if we want machine
			 * readable data and there is no data. */
			if (!MachineReadable)
			{
				printf(_("%s: %s, No GPS Data.\n"),
					File, Time);
			}
		}
	} else {
		/* Say that there was no data & return error, except if we want
		 * machine readable output */
		if (!MachineReadable)
		{
			printf(_("%s: No EXIF data.\n"), File);
			rc = 0;
		}
	}

	free(Time);

	if (MachineReadable) {
		setlocale(LC_NUMERIC, OldLocale);
	}

	return rc;
}
			
/* Remove all GPS exif tags from a file. Not really that useful, but... */
static int RemoveGPSTags(const char* File, int NoChangeMtime, int NoWriteExif)
{
	if (RemoveGPSExif(File, NoChangeMtime, NoWriteExif))
	{
		printf(_("%s: Removed GPS tags.\n"), File);
		return 1;
	} else {
		printf(_("%s: Tag removal failure.\n"), File);
		return 0;
	}
}

/* Fix GPSDatestamp tags, if they were incorrect, as found with versions
 * earlier than 1.5.2. */
static int FixDatestamp(const char* File, int AdjustmentHours, int AdjustmentMinutes, int NoWriteExif)
{
	/* Read the timestamp data. */
	char DateStamp[12];
	char TimeStamp[12];
	char CombinedTime[24];
	int IncludesGPS = 0;
	char* OriginalDateStamp = NULL;
	int rc = 1;

	OriginalDateStamp = ReadGPSTimestamp(File, DateStamp, TimeStamp, &IncludesGPS);

	if (OriginalDateStamp == NULL)
	{
		printf(_("%s: No EXIF data.\n"), File);
		rc = 0;
	} else if (IncludesGPS == 0) {
		printf(_("%s: No GPS data.\n"), File);
		rc = 0;
	} else {
		/* Check the timestamp. */
		time_t PhotoTime = ConvertToUnixTime(OriginalDateStamp, EXIF_DATE_FORMAT,
			AdjustmentHours, AdjustmentMinutes);
		
		snprintf(CombinedTime, sizeof(CombinedTime), "%s %s", DateStamp, TimeStamp);

		time_t GPSTime = ConvertToUnixTime(CombinedTime, EXIF_DATE_FORMAT, 0, 0);

		if (PhotoTime != GPSTime) {
			/* Timestamp is wrong. Fix it.
			 * Should be photo time - this also corrects
			 * GPSTimestamp, which was wrong too. */
			if (!NoWriteExif)
			{
				rc = WriteFixedDatestamp(File, PhotoTime);
			}
			char PhotoTimeFormat[100];
			char GPSTimeFormat[100];

			strftime(PhotoTimeFormat, sizeof(PhotoTimeFormat),
				 "%a %b %d %H:%M:%S %Y UTC", gmtime(&PhotoTime));
			strftime(GPSTimeFormat, sizeof(GPSTimeFormat),
				 "%a %b %d %H:%M:%S %Y UTC", gmtime(&GPSTime));
			printf(_("%s: Wrong timestamp:\n   Photo:     %s\n"
				 "   GPS:       %s\n   Corrected: %s\n"),
					File, PhotoTimeFormat, GPSTimeFormat, PhotoTimeFormat);
		} else {
			/* Inside the range. Do nothing! */
			printf(_("%s: Timestamp is OK: Photo %s (localtime), GPS %s (UTC).\n"),
			       File, OriginalDateStamp, CombinedTime);
		}
	}

	free(OriginalDateStamp);
	return rc;
}

int main(int argc, char** argv)
{
	/* Initialize locale & gettext */
	setlocale (LC_ALL, "");
	(void) bindtextdomain(TEXTDOMAIN, PACKAGE_LOCALE_DIR);
	(void) textdomain(TEXTDOMAIN);

	/* If you didn't pass any arguments... */
	if (argc == 1)
	{
		PrintVersion(argv[0]);
		PrintUsage(argv[0]);
		exit(EXIT_SUCCESS);
	}

	/* Parse our command line options. */
	/* But first, some variables to store stuff in. */
	int c;
	
	struct GPSTrack* Track = NULL;/* Array of lists of GPS waypoints. The
					 final entry of all 0 signals the end. */
	int NumTracks = 0;	     /* Number of track structures at Track,
					not including the terminating entry. */
	int HaveTimeAdjustment = 0;  /* Whether -z option was given. */
	int TimeZoneHours = 0;       /* Integer version of the timezone. */
	int TimeZoneMins = 0;
	char* Datum = NULL;          /* Datum of input GPS data. */
	int Interpolate = 1;         /* Do we interpolate? By default, yes. */
	int NoWriteExif = 0;         /* Do we not write to file? By default, no. */
	int OverwriteExisting = 0;   /* Do we overwrite existing tags? By default, no */
	int ShowDetails = 0;         /* Do we show lots of details? By default, no. */
	int FeatherTime = 0;         /* The "feather" time, in seconds. 0 = disabled. */
	int ShowOnlyDetails = 0;
	int MachineReadable = 0;
	int RemoveTags = 0;
	int DoBetweenTrackSegs = 0;
	int NoChangeMtime = 0;
	int FixDatestamps = 0;
	int DegMinSecs = 1;
	int PhotoOffset = 0;
	struct GPSPoint LatLong;

	/* Create the empty terminating array entry */
	Track = (struct GPSTrack*) calloc(1, sizeof(*Track));
	if (!Track)
	{
		fprintf(stderr, _("Out of memory.\n"));
		exit(EXIT_FAILURE);
	}

	while (1)
	{
		/* Call getopt to do all the hard work
		 * for us... */
		c = getopt_long(argc, argv, "g:z:il:hvd:m:nsortRMVfO:",
				program_options, 0);

		if (c == -1) break;

		/* Determine what getopt saw. */
		switch (c)
		{
			case 'g':
				/* This parameter specifies the GPS data.
				 * It or 'l' must be present at least once. */
				if (optarg)
				{
					int HaveTrack;

					/* Read the XML file into memory and extract the "points". */
					printf(_("Reading GPS Data..."));
					fflush(stdout);
					HaveTrack = ReadGPX(optarg, &Track[NumTracks]);
					printf("\n");
					if (!HaveTrack)
					{
						exit(EXIT_FAILURE);
					}

					/* Make room for a new end-of-array entry */
					++NumTracks;
					Track = (struct GPSTrack*) realloc(Track, sizeof(*Track)*(NumTracks+1));
					if (!Track)
					{
						fprintf(stderr, _("Out of memory.\n"));
						exit(EXIT_FAILURE);
					}
					memset(&Track[NumTracks], 0, sizeof(*Track));
				}
				break;
			case 'l':
				/* This parameter specifies a direct latitude/longitude
				   coordinate to use for all images.
				   It or 'g' must be present at least once. */
				if (!ParseLatLong(optarg, &LatLong))
				{
					printf(_("Error parsing location.\n"));
					exit(EXIT_FAILURE);
				}
				if (!MakeTrackFromLatLong(&LatLong, &Track[NumTracks]))
				{
					printf(_("Out of memory\n"));
					exit(EXIT_FAILURE);
				}

				/* Make room for a new end-of-array entry */
				++NumTracks;
				Track = (struct GPSTrack*) realloc(Track, sizeof(*Track)*(NumTracks+1));
				if (!Track)
				{
					printf(_("Out of memory\n"));
					exit(EXIT_FAILURE);
				}
				memset(&Track[NumTracks], 0, sizeof(*Track));
				break;

			case 'z':
				/* This parameter specifies the time to add to the
				 * GPS data to make it match the timezone for
				 * the photos. */
				/* We only store it here, convert it to numbers later. */
				if (optarg)
				{
					/* Break up the adjustment and convert to numbers. */
					if (strstr(optarg, ":"))
					{
						/* Found colon. Split into two. */
						sscanf(optarg, "%d:%d", &TimeZoneHours, &TimeZoneMins);
						if (TimeZoneHours < 0)
						    TimeZoneMins *= -1;
					} else {
						/* No colon. Just parse. */
						TimeZoneHours = atoi(optarg);
					}
					HaveTimeAdjustment = 1;
				}
				break;
			case 'O':
				if (optarg)
				{
					PhotoOffset = atoi(optarg);
				}
				break;
			case 'i':
				/* This option disables interpolation. */
				Interpolate = 0;
				break;
			case 'v':
				/* This option asks us to show more info. */
				PrintVersion(argv[0]);
				ShowDetails = 1;
				break;
			case 'd':
				/* This option specifies a Datum, if other than WGS-84. */
				if (optarg)
				{
					Datum = strdup(optarg);
				}
				break;
			case 'n':
				/* This option specifies not to write to file. */
				NoWriteExif = 1;
				break;
			case 'R':
				/* This option specifies to overwrite existing GPS tags. */
				OverwriteExisting = 1;
				break;
			case 'm':
				/* This option gives us the allowable "feather" time. */
				if (optarg)
				{
					FeatherTime = atoi(optarg);
				}
				break;
			case 'h':
				/* Display the help/usage information. And then quit. */
				PrintUsage(argv[0]);
				exit(EXIT_SUCCESS);
				break;
			case 'V':
				/* Display version information, and then quit. */
				PrintVersion(argv[0]);
				printf(_("This is free software; see the source for copying conditions.  There is NO\n"
					 "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"));
				exit(EXIT_SUCCESS);
				break;
			case 'f':
				/* Fix Datestamps. */
				FixDatestamps = 1;
				break;
			case 's':
				/* Show the data in the photos. Mark this. */
				ShowOnlyDetails = 1;
				break;
			case 'o':
				/* Show the data in the photos, machine readable. */
				ShowOnlyDetails = 1;
				MachineReadable = 1;
				break;
			case 'r':
				/* Remove GPS tags from the file. Mark this. */
				RemoveTags = 1;
				break;
			case 't':
				/* Interpolate between track segments. */
				DoBetweenTrackSegs = 1;
				break;
			case 'M':
				NoChangeMtime = 1;
				break;
			case 'p':
				/* Write in old DegMins format. */
				DegMinSecs = 0;
				break;
			case '?':
				/* Unrecognised option. Or, missing argument. */
				/* The user has already been informed, so just exit. */
				exit(EXIT_FAILURE);
				break;
			default:
				/* Unrecognised code that getopt returned anyway.
				 * Oops... */
				break;
		} /* End switch(c) */
	} /* End While(1) */
	
	/* Check to see if the user passed some files to work with. Not much
	 * good if they didn't. */
	if (optind < argc)
	{
		/* You passed some files. Handy! */
	} else {
		/* Hmm. It seems there were no other files... that doesn't work. */
		fprintf(stderr, _("At least one image file name must be given.\n"));
		exit(EXIT_FAILURE);
	}

	/* If we only wanted to display info on the passed photos, do so now. */
	if (ShowOnlyDetails)
	{
		int result = 1;
		while (optind < argc)
		{
			result = ShowFileDetails(argv[optind++], MachineReadable) && result;
		}
		exit(result ? EXIT_SUCCESS : EXIT_FAILURE);
	}

	/* If we wanted to delete tags, do this now. */
	if (RemoveTags)
	{
		int result = 1;
		while (optind < argc)
		{
			result = RemoveGPSTags(argv[optind++], NoChangeMtime, NoWriteExif) && result;
		}
		exit(result ? EXIT_SUCCESS : EXIT_FAILURE);
	}

	/* If we wanted to fix datestamps, do this now.
	 * This is to fix incorrect GPSDateStamp values written by versions
	 * earlier than 1.5.2. */
	if (FixDatestamps)
	{
		if (!HaveTimeAdjustment)
		{
			fprintf(stderr, _("A time offset must be given with the -z option to fix photos.\n"));
			exit(EXIT_FAILURE);
		}
		
		int result = 1;
		while (optind < argc)
		{
			result = FixDatestamp(argv[optind++], TimeZoneHours, TimeZoneMins, NoWriteExif) && result;
		}
		exit(result ? EXIT_SUCCESS : EXIT_FAILURE);
	}

	/* Set up any other command line options... */
	if (!Datum)
	{
		Datum = strdup("WGS-84");
	}

	if (!NumTracks)
	{
		/* GPS Data was not read correctly... */
		/* Tell the user we are bailing.
		 * Not really required, seeing as ReadGPX should
		 * inform the user anyway... but, doesn't hurt! */
		fprintf(stderr, _("Cannot continue since no GPS data is available.\n"));
		exit(EXIT_FAILURE);
	}

	/* Print a legend for the matching process.
	 * If we're not being verbose. Otherwise, this would be pointless. */
	if (!ShowDetails)
	{
		printf(_("Legend: . = Ok, / = Interpolated, < = Rounded, - = No match, ^ = Too far\n"
			 "        w = Write Fail, ? = No EXIF date, ! = GPS already present\n"));
	}

	/* Set up our options structure for the correlation function. */
	struct CorrelateOptions Options;
	Options.NoWriteExif   = NoWriteExif;
	Options.OverwriteExisting = OverwriteExisting;
	Options.NoInterpolate = (Interpolate ? 0 : 1);
	Options.AutoTimeZone  = !HaveTimeAdjustment;
	Options.TimeZoneHours = TimeZoneHours;
	Options.TimeZoneMins  = TimeZoneMins;
	Options.FeatherTime   = FeatherTime;
	Options.Datum         = Datum;
	Options.DoBetweenTrkSeg = DoBetweenTrackSegs;
	Options.NoChangeMtime = NoChangeMtime;
	Options.DegMinSecs    = DegMinSecs;
	Options.PhotoOffset   = PhotoOffset;

	Options.Track         = Track;

	/* Make it all look nice and pretty... so the user knows what's going on. */
	printf(_("\nCorrelate: "));
	if (ShowDetails) printf("\n");
	
	/* A few variables that we'll require later. */
	struct GPSPoint* Result;
	char* File;
	/* Including stats on what happened. */
	int MatchExact = 0;
	int MatchInter = 0;
	int MatchRound = 0;
	int NotMatched = 0;
	int WriteFail  = 0;
	int TooFar     = 0;
	int NoDate     = 0;
	int GPSPresent = 0;

	/* Now it is time to correlate the photos. Feed one in at a time, and
	 * see what happens.*/
	/* We already checked to make sure that files were passed on the
	 * command line, so just go for it... */
	/* printf("Remaining non-option arguments: %d.\n", argc - optind); */
	while (optind < argc)
	{
		File = argv[optind++];
		/* Pass the file along to Correlate and see what happens. */
		Result = CorrelatePhoto(File, &Options);

		/* Was result NULL? */
		if (Result)
		{
			/* Result not null. But what did happen? */
			if (Options.Result == CORR_OK)
			{
				MatchExact++;
				if (ShowDetails)
				{
					printf(_("%s: Exact match: "), File);
				} else {
					printf(".");
				}
			}
			if (Options.Result == CORR_INTERPOLATED)
			{
				MatchInter++;
				if (ShowDetails)
				{
					printf(_("%s: Interpolated: "), File);
				} else {
					printf("/");
				}
			}
			if (Options.Result == CORR_ROUND)
			{
				MatchRound++;
				if (ShowDetails)
				{
					printf(_("%s: Rounded: "), File);
				} else {
					printf("<");
				}
			}
			if (Options.Result == CORR_EXIFWRITEFAIL)
			{
				WriteFail++;
				if (ShowDetails)
				{
					printf(_("%s: EXIF write failure: "), File);
				} else {
					printf("w");
				}
			}
			if (ShowDetails)
			{
				/* Print out the "point". */
				printf(_("Lat %f, Long %f, Elev "),
					Result->Lat, Result->Long);
				if (Result->ElevDecimals >=0)
					printf("%.3f.\n", Result->Elev);
				else
					printf(_("(unknown).\n"));
			}
			free(Result);
			/* Ok, that's all from this part... */
		} else {
			/* We got nothing back. One of a few errors. */
			if (Options.Result == CORR_NOMATCH)
			{
				NotMatched++;
				if (ShowDetails)
				{
					printf(_("%s: No match.\n"), File);
				} else {
					printf("-");
				}
			}
			if (Options.Result == CORR_TOOFAR)
			{
				TooFar++;
				if (ShowDetails)
				{
					printf(_("%s: Too far from nearest point.\n"), File);
				} else {
					printf("^");
				}
			}
			if (Options.Result == CORR_NOEXIFINPUT)
			{
				NoDate++;
				if (ShowDetails)
				{
					printf(_("%s: No EXIF date tag present.\n"), File);
				} else {
					printf("?");
				}
			}
			if (Options.Result == CORR_GPSDATAEXISTS)
			{
				GPSPresent++;
				if (ShowDetails)
				{
					printf(_("%s: GPS Data already present.\n"), File);
				} else {
					printf("!");
				}
			}
			/* Handled all those errors, now... */
		} /* End if Result. */

		/* Display the character code immediately */
		fflush(stdout);

		/* And, once we've got here, we've finished with that file.
		 * We can now do the next one. Now wasn't that too easy? */
		
	} /* End while parse command line files. */
	
	/* Right, so now we're done. That really wasn't that hard. Right? */

	/* Add a new line if we were doing the not-show-details thing. */
	if (!ShowDetails)
	{
		printf("\n");
	}

	/* Print details of what happened. */
	printf(_("\nCompleted correlation process.\n"));
	if (ShowDetails)
		/* This has to be shown at the end in case auto time zone
		 * was used, since it isn't known before the first file
		 * is processed. */
		printf(_("Used time zone offset %d:%02d\n"),
		       Options.TimeZoneHours, abs(Options.TimeZoneMins));
	printf(_("Matched: %5d (%d Exact, %d Interpolated, %d Rounded).\n"),
			MatchExact + MatchInter + MatchRound,
			MatchExact, MatchInter, MatchRound);
	printf(_("Failed:  %5d (%d Not matched, %d Write failure, %d Too Far,\n"),
			NotMatched + WriteFail + TooFar + NoDate + GPSPresent,
			NotMatched, WriteFail, TooFar);
	printf(_("                %d No Date, %d GPS Already Present.)\n"),
			NoDate, GPSPresent);


	/* Clean up! */
	while (NumTracks > 0)
	{
		--NumTracks;
		FreeTrack(&Track[NumTracks]);
	}
	free(Track);
	free(Datum);
	
	if (WriteFail)
		/* A write failure is considered serious */
		return EXIT_FAILURE;

	/* Other failures aren't necessarily bad, depending on the input,
	 * so provide a different return code to distinguish them.
	 */
	return(NotMatched + TooFar + NoDate + GPSPresent ? GPS_EXIT_WARNING : EXIT_SUCCESS);
}

