/*----------------------------------------------------------------------*\
 | Program to implement the modern analog method of estimating past		|
 | environmental parameters by comparing characteristics of fossil		|
 | samples with those of the modern world.								|
 |																		|
 | Peter N. Schweitzer (U.S. Geological Survey, Reston, VA 22092)		|
\*----------------------------------------------------------------------*/

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

#include "analog.h"

struct data_base *modern = NULL;
struct data_base *fossil = NULL;
struct output report = {"","",1,0,0,NULL};
char distance_measure_name [MAX_NAME_LENGTH] = "squared_chord";
enum measure_type_t measure_type = DISTANCE;
int verbose = 0;
int search = 1;

/*----------------------------------------------------------------------*\
 | Given a pair of data bases, create a table that lists matching		|
 | variable names.  This is used to ensure that the distances are		|
 | calculated on the same axes.											|
\*----------------------------------------------------------------------*/

static struct match *create_match_table (struct data_base *p, struct data_base *q) {
	int i,j,k,n;
	int d;
	struct match *match = NULL;

	n = min (p->data.count,q->data.count);
	if (match = (struct match *) malloc ((1 + n) * sizeof (struct match))) {
		i = j = k = 0;
		while (i < p->data.count && j < q->data.count) {
			if ((d = strcmp (p->data.name[i],q->data.name[j])) < 0) {
				sprintf (message,"Warning: data from %s contains variable \"%s\" not found in %s",
					p->raw.filespec,
					p->data.name[i],
					q->raw.filespec
					);
				warning (message);
				i++;
				}
			else
				if (d > 0) {
					sprintf (message,"Warning: data from %s contains variable \"%s\" not found in %s",
						q->raw.filespec,
						q->data.name[j],
						p->raw.filespec
						);
					warning (message);
					j++;
					}
				else {
					match[k].p = i++;
					match[k].q = j++;
					k++;
					}
			}
		match[k].p = match[k].q = -1;
		}
	else {
		sprintf (message,"Error: could not allocate space for match table");
		error_exit (message);
		}
	return (match);
	}

/*----------------------------------------------------------------------*\
 | Sorting comparison functions; dcompare sorts distance measures so	|
 | the key values of the resulting array increase, and scompare sorts	|
 | similarity measures, so the key values of the result will decrease.	|
\*----------------------------------------------------------------------*/

static int dcompare (const void *e1, const void *e2) {
	struct comparison *L1,*L2;

	L1 = (struct comparison *) e1;
	L2 = (struct comparison *) e2;

	if (L1->d < L2->d) return (-1);
	else
		if (L1->d > L2->d) return (1);
		else
			return (0);
	}

static int scompare (const void *e1, const void *e2) {
	struct comparison *L1,*L2;

	L1 = (struct comparison *) e1;
	L2 = (struct comparison *) e2;

	if (L1->d < L2->d) return (1);
	else
		if (L1->d > L2->d) return (-1);
		else
			return (0);
	}

/*----------------------------------------------------------------------*\
\*----------------------------------------------------------------------*/

main (int argc, char *argv[]) {
	int i,j,k;
	char *job_file = NULL;
	distance_function distance;
	int sample_count;
	struct comparison *score = NULL;
	struct data_base *p,*q;
	struct sample *f,*m;
	struct match *match;
	int (*compare) (const void *e1, const void *e2);

	if (argc <= 1) {
		printf ("%s\n",version);
		printf ("\n");
		printf ("A program for estimating paleoclimate parameters\n");
		printf ("using the method of modern analogs\n");
		printf ("\n");
		printf ("Peter N. Schweitzer (U.S. Geological Survey, Reston VA 20192)\n");
		printf ("\n");
		printf ("  Usage: name run description file on the command line\n");
		exit (0);
		}
	else
		for (i=1; i < argc; i++)
			if (memcmp (argv[i],"-search",7) == 0) search = 1;
			else
				if (memcmp (argv[i],"-nosearch",7) == 0) search = 0;
				else
					job_file = argv[i];

	/*------------------------------------------------------------------*\
	 | If no forward slashes appear in the file name, then assume this	|
	 | is a Windows system, and replace backslashes with forwards.		|
	\*------------------------------------------------------------------*/

	if (strchr (job_file,'/') == NULL)
		for (i=0; job_file[i]; i++)
			if (job_file[i] == '\\') job_file[i] = '/';

	parse_run_configuration (job_file);

	/*------------------------------------------------------------------*\
	\*------------------------------------------------------------------*/

	if (verbose) {
		printf ("\n");
		printf ("%s\n",version);
		printf ("\n");
		for (p=modern,i=0; p; p=p->next,i++) {
			printf ("Modern data base %d:\n",i+1);
			printf ("  raw data filespec:  \"%s\"\n",p->raw.filespec);
			if (p->raw.format) printf ("           format:    \"%s\"\n",p->raw.format);
			printf ("  transform filespec: \"%s\"\n",p->rule.filespec);
			printf ("  meta data filespec: \"%s\"\n",p->meta.filespec);
			if (p->meta.format) printf ("            format:   \"%s\"\n",p->meta.format);
			}
		printf ("\n");
		for (p=fossil,i=0; p; p=p->next,i++) {
			printf ("Fossil data base %d:\n",i+1);
			printf ("  raw data filespec:  \"%s\"\n",p->raw.filespec);
			if (p->raw.format) printf ("           format:    \"%s\"\n",p->raw.format);
			printf ("  transform filespec: \"%s\"\n",p->rule.filespec);
			printf ("  meta data filespec: \"%s\"\n",p->meta.filespec);
			if (p->meta.format) printf ("            format:   \"%s\"\n",p->meta.format);
			i++;
			}
		printf ("\n");
		printf ("Distance measure: \"%s\"\n",distance_measure_name);
		printf ("\n");
		printf ("Output\n");
		printf ("  filespec: \"%s\"\n",report.filespec);
		printf ("  format:   \"%s\"\n",report.format);
		printf ("  closest %d\n",report.closest);
		if (report.count) printf ("  meta data variables to report:\n");
		for (i=0; i < report.count; i++)
			printf ("    \"%s\"\n",report.name[i]);
		}

	/*------------------------------------------------------------------*\
	 | Assign the function pointer "distance" by looking up the name of	|
	 | the distance measure in a table that relates distance functions	|
	 | to the names of their distance measures.							|
	\*------------------------------------------------------------------*/

	distance = set_distance_function (distance_measure_name);
	measure_type = get_measure_type (distance_measure_name);

	/*------------------------------------------------------------------*\
	 | Read the data for the modern samples.  Read the transformation	|
	 | file, if any, the meta data file, if any, and transform the raw	|
	 | data if necessary.												|
	\*------------------------------------------------------------------*/

	for (p=modern,i=0; p; p=p->next,i++) read_data_base (p);

	/*------------------------------------------------------------------*\
	 | Read the data for the fossil samples.  Read the transformation	|
	 | file, if any, the meta data file, if any, and transform the raw	|
	 | data if necessary.												|
	\*------------------------------------------------------------------*/

	for (p=fossil,i=0; p; p=p->next,i++) read_data_base (p);

	/*------------------------------------------------------------------*\
	 | If the requested output meta data list contains the word "all",	|
	 | then we must build a name list that will work for all of the		|
	 | modern data bases that were read in.								|
	\*------------------------------------------------------------------*/

	for (i=0; i < report.count; i++)
		if (stricmp (report.name[i],"all") == 0) break;
	if (i < report.count) {

		/*--------------------------------------------------------------*\
		 | "all" occurred in the report.name list.  Destroy the list	|
		 | and rebuild it, using p->meta.name[j] for all j, for all p.	|
		\*--------------------------------------------------------------*/

		for (k=0; k < report.count; k++)
			if (report.name[k]) {
				free (report.name[k]);
				report.name[k] = NULL;
				}
		report.count = 0;

		for (p=modern,i=0; p; p=p->next,i++) {
			for (j=0; j < p->meta.count; j++) {

				/*------------------------------------------------------*\
				 | Determine whether p->meta.name[j] is already in the	|
				 | list.  The list is not sorted, so the search must be	|
				 | linear.												|
				\*------------------------------------------------------*/

				for (k=0; k < report.count; k++)
					if (strcmp (p->meta.name[j],report.name[k]) == 0) break;
				if (k == report.count) {
					report.name[report.count++] = p->meta.name[j];
					if (report.count >= report.limit) {
						if (report.name = (char **) realloc (report.name,(report.limit + GRANULARITY) * sizeof (char *)))
							report.limit += GRANULARITY;
						else {
							sprintf (message,"Error: could not enlarge space for output meta data names");
							error_exit (message);
							}
						}
					}
				}
			}
		}

	/*------------------------------------------------------------------*\
	 | For each fossil sample, calculate its distance from each modern	|
	 | sample.  Sort the modern samples according to their similarity	|
	 | with the fossil sample.  Write results as requested by the user.	|
	\*------------------------------------------------------------------*/

	if (measure_type == DISTANCE) compare = dcompare;
	else compare = scompare;

	sample_count = 0;
	for (p=modern; p; p=p->next) sample_count += p->count;

	if (score = (struct comparison *) malloc (sample_count * sizeof (struct comparison))) {
		match = NULL;
		for (q=fossil; q; q=q->next) {
			f = q->sample;
			for (i=0; i < q->count; i++) {
				j = 0;
				for (p=modern; p; p=p->next) {
					if (i == 0) {
						if (match) free (match);
						match = create_match_table (q,p);
						}
					m = p->sample;
					for (k=0; k < p->count; k++) {
						score[j].s = m;
						score[j].d = distance (f,m,match);
						j++;
						m++;
						}
					}
				qsort (score,sample_count,sizeof(struct comparison),compare);
				write_results (f,score);
				f++;
				}
			}
		free (score);
		}
	else {
		sprintf (message,"Error: could not allocate space for comparison array");
		error_exit (message);
		}
	}

/*----------------------------------------------------------------------*\
\*----------------------------------------------------------------------*/