/*--------------------------------------------------------------------
 *    $Id: minmax.c,v 1.3.4.3 2002/02/27 17:41:10 pwessel Exp $
 *
 *	Copyright (c) 1991-2002 by P. Wessel and W. H. F. Smith
 *	See COPYING file for copying and redistribution conditions.
 *
 *	This program 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; version 2 of the License.
 *
 *	This program 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.
 *
 *	Contact info: gmt.soest.hawaii.edu
 *--------------------------------------------------------------------*/
/*
 * minmax.c will read ascii or binary tables and report the
 * extreme values for all columns
 *
 * Author:	Paul Wessel
 * Date:	20-JUN-2000
 * Revised:	20-FEB-2001 BCH-J: Added -D option for dateline discontinuity
 * Version:	3.4.1
 */

#include "gmt.h"

main (int argc, char **argv)
{
	int n, i, ncol, n_files = 0, fno, n_args, n_fields, n_expected_fields;

	BOOLEAN  error = FALSE, nofile = TRUE, done = FALSE, got_stuff = FALSE, first, step = FALSE, columns = FALSE, give_r_string = FALSE;
	BOOLEAN  dateline = FALSE;

	double delta[2], *xyzmin, *xyzmax, west, east, south, north, low, high, *in;

	char line[BUFSIZ], file[BUFSIZ], format[BUFSIZ], empty_format[15];

	FILE *fp = NULL;
	
	argc = GMT_begin (argc, argv);
	
	for (i = 1; i < argc; i++) {
		if (argv[i][0] == '-') {
			switch (argv[i][1]) {
				/* Common parameters */
                      
				case 'H':
				case ':':
				case '\0':
					error += GMT_get_common_args (argv[i], 0, 0, 0, 0);
					break;

				/* Supplemental parameters */

  				case 'b':
					error += GMT_io_selection (&argv[i][2]);
					break;
				case 'C':
					columns = TRUE;
					break;
                                case 'D':
                                        dateline = TRUE;
                                        break;
				case 'M':
					GMT_multisegment (&argv[i][2]);
					break;
				case 'I':
					GMT_getinc (&argv[i][2], &delta[0], &delta[1]);
					step = TRUE;
					break;
				case 'L':
					GMT_geographic_in = TRUE;
					break;
				default:
					error = TRUE;
					GMT_default_error (argv[i][1]);
					break;
			}
		}
		else
			n_files++;
	}
	
	if (error || GMT_quick) {	/* Because it's ok to give no arguments */
		fprintf (stderr, "minmax %s - Find extreme values in ASCII tables\n\n", GMT_VERSION);
		fprintf (stderr, "usage: minmax [files] [-C] [-D] [-H[<nrec>]] [-Idx[/dy]] [-L] [-M[<flag>]] [-:] [-bi[s][<n>]\n");
             
              	if (GMT_quick) exit (EXIT_FAILURE);
 
		fprintf (stderr, "\t-C formats the min and max into separate columns\n");
		fprintf (stderr, "\t-D sets longitude discontinuity to dateline (requires -L)\n");
		GMT_explain_option ('H');
		fprintf (stderr, "\t-I returns textstring -Rw/e/s/n to nearest multiple of dx/dy (assumes 2+ col data)\n");
		fprintf (stderr, "\t   If -C is set then only the first 2 columns are rounded off and no -R string is issued\n");
		fprintf (stderr, "\t-L means that x is longitude, i.e. assumed to be periodic in 360\n");
		GMT_explain_option ('M');
		GMT_explain_option (':');
		GMT_explain_option ('i');
		GMT_explain_option ('n');
		fprintf (stderr, "\t   Default is 2 input columns\n");
		GMT_explain_option ('.');
		exit (EXIT_FAILURE);
	}
	
	if (step && (delta[0] <= 0.0 || delta[1] <= 0.0)) {
		fprintf (stderr, "%s: GMT SYNTAX ERROR -I option.  Must specify positive increment(s)\n", GMT_program);
		error++;
	}
	if (GMT_io.binary[0] && gmtdefs.io_header) {
		fprintf (stderr, "%s: GMT SYNTAX ERROR.  Binary input data cannot have header -H\n", GMT_program);
		error++;
	}
	if (GMT_io.binary[0] && GMT_io.ncol[0] == 0) GMT_io.ncol[0] = 2;
	if (GMT_io.binary[0] && GMT_io.ncol[0] < 1) {
		fprintf (stderr, "%s: GMT SYNTAX ERROR.  Binary input data (-bi) must have at least 1 column\n", GMT_program);
		error++;
	}
	if (! GMT_geographic_in && dateline) {
	  	fprintf (stderr, "%s: GMT SYNTAX ERROR -D option: requires -L\n", GMT_program);
		error++;
	}
	if (error) exit (EXIT_FAILURE);

	GMT_put_history (argc, argv);	/* Update .gmtcommands */

	if (GMT_io.binary[0] && gmtdefs.verbose) {
		char *type[2] = {"double", "single"};
		fprintf (stderr, "%s: Expects %d-column %s-precision binary data\n", GMT_program, GMT_io.ncol[0], type[GMT_io.single_precision[0]]);
	}

	if (n_files > 0)
		nofile = FALSE;
	else
		n_files = 1;
	
	n_args = (argc > 1) ? argc : 2;
	west = south = DBL_MAX;	east = north = -DBL_MAX;
	
	xyzmin = (double *) GMT_memory (VNULL, (size_t)1, sizeof (double), GMT_program);
	xyzmax = (double *) GMT_memory (VNULL, (size_t)1, sizeof (double), GMT_program);
	
	if (step && !columns) {
		give_r_string = TRUE;
		sprintf (format, "-R%s/%s/%s/%s\n", gmtdefs.d_format, gmtdefs.d_format, gmtdefs.d_format, gmtdefs.d_format);
	}
	else if (columns) {
		sprintf (format, "\t%s\t%s", gmtdefs.d_format, gmtdefs.d_format);
		strcpy (empty_format, "\tNaN\tNaN");
	}
	else {
		sprintf (format, "\t<%s/%s>", gmtdefs.d_format, gmtdefs.d_format);
		strcpy (empty_format, "\t<NaN/NaN>");
	}

	n_expected_fields = (GMT_io.ncol[0]) ? GMT_io.ncol[0] : BUFSIZ;

	for (fno = 1; !done && fno < n_args; fno++) {     /* Loop over input files, if any */
		if (!nofile && argv[fno][0] == '-') continue;

		
		if (nofile) {   /* Just read standard input */
			fp = GMT_stdin;
			done = TRUE;
			strcpy (file, "<stdin>");
			if (gmtdefs.verbose) fprintf (stderr, "%s: Reading from standard input\n", GMT_program);
#ifdef SET_IO_MODE
			GMT_setmode (0);
#endif
		}
		else {
			strcpy (file, argv[fno]);
			if ((fp = GMT_fopen (file, GMT_io.r_mode)) == NULL) {
				fprintf (stderr, "%s: Cannot open file %s\n", GMT_program, file);
				continue;
			}
		}
		
		if (gmtdefs.io_header) for (i = 0; i < gmtdefs.n_header_recs; i++) GMT_fgets (line, BUFSIZ, fp);
		
		n = ncol = 0;
		n_expected_fields = (GMT_io.ncol[0]) ? GMT_io.ncol[0] : BUFSIZ;
		first = TRUE;

		n_fields = GMT_input (fp, &n_expected_fields, &in);

		while (! (GMT_io.status & GMT_IO_EOF)) {	/* Not yet EOF */

			while ((GMT_io.status & GMT_IO_SEGMENT_HEADER) && !(GMT_io.status & GMT_IO_EOF)) {
					n_fields = GMT_input (fp, &n_expected_fields, &in);
			}
			if ((GMT_io.status & GMT_IO_EOF)) continue;	/* At EOF */

			if (GMT_io.status & GMT_IO_MISMATCH) {
				fprintf (stderr, "%s: Mismatch between actual (%d) and expected (%d) fields near line %d\n", GMT_program, n_fields, n_expected_fields, n);
				exit (EXIT_FAILURE);
			}

			if (first) {	/* First time, allocate # of columns */
			
				ncol = n_expected_fields;

				/* Now we know # of columns, so allocate memory */
			
				xyzmin = (double *) GMT_memory ((void *)xyzmin, (size_t)ncol, sizeof (double), GMT_program);
				xyzmax = (double *) GMT_memory ((void *)xyzmax, (size_t)ncol, sizeof (double), GMT_program);
			
				for (i = 0; i < ncol; i++) {	/* Initialize */
					xyzmin[i] = +DBL_MAX;
					xyzmax[i] = -DBL_MAX;
				}
				if (step && ncol < 2 && !columns) step = FALSE;
				first = FALSE;
			}
		
			/* Decode all fields and update minmax arrays */

			while (dateline && in[0] > 180.0) in[0] -= 360.0;

			for (i = 0; i < ncol; i++) {
				if (GMT_is_dnan (in[i])) continue;
				if (in[i] < xyzmin[i]) xyzmin[i] = in[i];
				if (in[i] > xyzmax[i]) xyzmax[i] = in[i];
			}
				
			n++;

			n_fields = GMT_input (fp, &n_expected_fields, &in);
		}
		if (fp != GMT_stdin) GMT_fclose (fp);
		
		if (!got_stuff) got_stuff = (n > 0);	/* We were able to open and read at least 1 record from a file */

		if (give_r_string && got_stuff) {
			west  = MIN (west, xyzmin[0]);		east  = MAX (east, xyzmax[0]);
			south = MIN (south, xyzmin[1]);		north = MAX (north, xyzmax[1]);
		}
		else if (n > 0) {
			if (!columns) fprintf (GMT_stdout, "%s: N = %d", file, n);
			for (i = 0; i < ncol; i++) {
				if (xyzmin[i] == DBL_MAX)
					fprintf (GMT_stdout, "%s", empty_format);
				else if (i < 2) {
					low  = (step) ? floor (xyzmin[i] / delta[i]) * delta[i] : xyzmin[i];
					high = (step) ? ceil (xyzmax[i] / delta[i]) * delta[i] : xyzmax[i];
					fprintf (GMT_stdout, format, low, high);
				}
				else {
					fprintf (GMT_stdout, format, xyzmin[i], xyzmax[i]);
				}
			}
			fprintf (GMT_stdout, "\n");
		}
	}
	if (got_stuff && give_r_string) {
		west  = floor (west / delta[0]) * delta[0];	east  = ceil (east / delta[0]) * delta[0];
		south = floor (south / delta[1]) * delta[1];	north = ceil (north / delta[1]) * delta[1];
		fprintf (GMT_stdout, format, west, east, south, north);
	}
	else if (!got_stuff)
		fprintf (stderr, "%s: No input data found!\n", GMT_program);
	
	GMT_free ((void *)xyzmin);
	GMT_free ((void *)xyzmax);
	
	GMT_end (argc, argv);
}
