/*
 * © Copyright 1996-2012 ECMWF.
 *
 * This software is licensed under the terms of the Apache Licence Version 2.0
 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
 * In applying this licence, ECMWF does not waive the privileges and immunities
 * granted to it by virtue of its status as an intergovernmental organisation nor
 * does it submit to any jurisdiction.
 */

#include <ctype.h>
#include <time.h>
#include <stdarg.h>
#include <stdlib.h>
#include "mars.h"
#include "lang.h"
#include <setjmp.h>
#include <signal.h>



static jmp_buf env;

static void catch_alarm(int sig)
{
	longjmp(env,1);
}

/* This function should not exist, only for historical reasons.
   Once everybody uses ranges, it can dissapear */
static void days2range(const char *l, stepRange* r, boolean waves, boolean sfc)
{
	long step = atol(l);
	switch(step)
	{
		case 507:              /* In grib header 132-168 */
			/* r->from = 120; */
			r->from = 132;
			r->to   = 168;
			break;
		case 710:              /* In grib header 180-240 */
			/* r->from = 168; */
			r->from = 180;
			r->to   = 240;
			break;
		case 510:              /* In grib header 132-240 */
			/* r->from = 120; */
			r->from = 132;
			r->to   = 240;
			break;
		case 607:              /* In grib header 120-168 */
			if(sfc && mars.no_special_fp)
				r->from = 120;
			else
				r->from =  144;
			r->to   = 168;
			break;
		case 810:              /* In grib header 168-240 */
			if(sfc && mars.no_special_fp)
				r->from = 168;
			else
				r->from = 192;
			r->to   = 240;
			break;
		case 610:              /* In grib header 120-240 */
			if(sfc && mars.no_special_fp)
				r->from = 120;
			else
				r->from = 144;
			r->to   = 240;
			break;
		case 1015:
			if(sfc && mars.no_special_fp)
				r->from = 240;
			else
				r->from = 252;
			r->to = 360;
			break;
		case 1115:
			r->from = 264;
			r->to = 360;
			break;
		default:
			marslog(LOG_WARN,"Unknown range of days %ld",step);
	}
/*
	if(waves)
		r->from += 12;
*/
}

/* Converts a string into a range when '-' found */
void str2range(const char *l, stepRange* r)
{
	const char *p = l+strlen(l);
	r->from  = 0;
	r->to    = atol(l);

	/* If there's a -, process it as a range */
	/* COME BACK HERE */
	while(p>l)
	{
		if(*p == '-' || *p == ':')
		{
			++p;
			r->from = atol(l);
			r->to   = atol(p);
			break;
		}
		--p;
	}
}

void patch_steprange(request *r)
{
	const char *type = get_value(r,"TYPE",0);

	if(type && EQ(type,"FA"))
	{
		int i = 0;
		int n = count_values(r,"STEP");
		request *s = empty_request("EMPTY");
		for(i = 0; i<n; ++i)
		{
			const char *prev = get_value(r,"STEP",i);
			stepRange range;

			if(EQ(prev,"ALL"))
			{
				add_value(s,"STEP","ALL");
			}
			else
			{
				str2range(prev,&range);
				add_value(s,"STEP","%ld-%ld",range.from,range.to);
			}
		}
		valcpy(r,s,"STEP","STEP");
		free_all_requests(s);
	}

	if(type && EQ(type,"FP"))
	{
		int i = 0;
		int n = count_values(r,"STEP");
		request *s = empty_request("EMPTY");
		for(i = 0; i<n; ++i)
		{
			const char *prev = get_value(r,"STEP",i);

			/* Change syntax fromdaytoday into fromstep-tostep */
			if((strlen(prev) == 4) && !isrange(prev))
			{
				const char *stream = get_value(r,"STREAM",0);
				const char *levtype = get_value(r,"LEVTYPE",0);
				boolean waves = (stream && (strcmp(stream,"WAEF")==0));
				boolean sfc = (levtype && (strcmp(levtype,"SFC")==0));
				stepRange range;
				days2range(prev,&range, waves, sfc);
				marslog(LOG_DBUG,"patch_steprange: convert step '%s' into '%ld-%ld'",prev,range.from,range.to);
				add_value(s,"STEP","%ld-%ld",range.from,range.to);
			}
			else
			{
				marslog(LOG_DBUG,"patch_steprange: keep step '%s'",prev);
				add_value(s,"STEP","%s",prev);
			}
		}
		valcpy(r,s,"STEP","STEP");
		free_all_requests(s);
	}
}

boolean eq_range(const char *l, const char *r)
{
	stepRange lrange, rrange;

	if(l && r)
	{
		str2range(l,&lrange);
		str2range(r,&rrange);

		marslog(LOG_DBUG,"Compared range [%ld,%ld] and [%ld,%ld]",
				lrange.from,lrange.to,rrange.from,rrange.to);

		return ((lrange.from == rrange.from) && (lrange.to == rrange.to));
	}

	return false;
}

#define NO_TABLE -1
#define NO_PARAM  0

void paramtable(const char *p, long *param, long *table, boolean paramIdMode)
{
	const char *q = p;
	int len = strlen(p);

	*param = atol(p);

	while(p && (*p != '.') && ((p-q)<len))
		++p;

	if((*p == '.'))
		*table = atol(++p);

/* This version is grib_api... It should rely on what grib_api returns,
   either param.table or paramId
*/
	if(paramIdMode)
	{
		/* Special case for param=228015 => 15.228 */
		/* if((*param != NO_PARAM) && (*table == NO_TABLE) && (len==6)) */
		if((*param != NO_PARAM) && (*table == NO_TABLE) && (len>3))
		{
			/* Note table is not always 3 digits, it could be 1 for tables < 10. Keep it in 'n' */
			/* param must be always 3 digits */
			int n = len - 3;
			p = q;
			char tbl[4];
			char par[4];
			strncpy(tbl,p,n); tbl[3]='\0';
			strncpy(par,p+n,3); par[3]='\0';
			*param = atol(par);
			*table = atol(tbl);
		}
	}
	marslog(LOG_DBUG,"Translate %s => param=%ld, table=%ld",q,*param,*table);
}


boolean eq_param(const char *l, const char *r)
{
	if(l && r)
	{
		long lpar = NO_PARAM;
		long ltable = NO_TABLE;
		long rpar = NO_PARAM;
		long rtable = NO_TABLE;

		paramtable(l,&lpar,&ltable,true);
		paramtable(r,&rpar,&rtable,true);

		if((ltable != NO_TABLE) && (rtable != NO_TABLE) && (ltable!=rtable))
			return false;

		return (lpar == rpar);
	}
	return false;
}

boolean eq_string(const char *l, const char *r)
{
	if(l && r)
		return l == r;
	return false;
}

boolean eq_integer(const char *l, const char *r)
{
	if(l && r)
		return atol(l) == atol(r);
	return false;
}

boolean eq_real(const char *l, const char *r)
{
	if(l && r)
		return atof(l) == atof(r);
	return false;
}

boolean eq_coord(const char *l, const char *r)
{
	if(l && r)
	{
		if(EQ(l,r)) return true;

#ifndef linux
		return round_decimal(atof(l)) == round_decimal(atof(r));
#else
/*
   Warning: This code optimized on linux (-O1,2,3) gives rounding errors while
            calling routine round_decimal. If the code is included here, the
			error dissapears.

*/
		{
			double ld = atof(l);
			double rd = atof(r);
			double x;

			ld = ((long)((1000.0 * ld) + 0.5)) / 1000.0;
			rd = ((long)((1000.0 * rd) + 0.5)) / 1000.0;
			x = ld - rd;
			marslog(LOG_DBUG,"%g, l=%s, r=%s",x,l,r);

			return x == 0;
		}
#endif
	}
	return false;
}

boolean eq_null(const char *l, const char *r)
{
	return true;
}

static long name_to_date(const char *, boolean ,boolean* );
static boolean get_date_parts(const char* name,int *year, int* month, int* day);

boolean eq_date(const char *l, const char *r)
{
	if(l && r)
	{
		boolean isjul = false;
		long lndate;
		long rndate;

		/* marslog(LOG_INFO,"eq_date(%s,%s)",l,r); */

		if(isalpha(l[0]) || isalpha(r[0]))
		{
			int ly = 0,lm = 0,ld = 0;
			int ry = 0,rm = 0,rd = 0;
			int lp = get_date_parts(l,&ly,&lm,&ld);
			int rp = get_date_parts(r,&ry,&rm,&rd);

			/* marslog(LOG_INFO,"eq_date(%d,%d)",lp,rp); */


			if(lp && !rp) {
				rndate = mars_julian_to_date(name_to_date(r,false,&isjul), mars.y2k);
				if(ld) { rd = rndate % 100; }
				if(lm) { rm = (rndate/100) % 100; }
			}

			if(!lp && rp) {
				lndate = mars_julian_to_date(name_to_date(l,false,&isjul), mars.y2k);
				if(rd) { ld = lndate % 100; }
				if(rm) { lm = (lndate/100) % 100; }
			}

			return (ly == ry) && (lm == rm) && (ld == rd);

		}

		/* What is that ? */
#if 0
		if((strlen(l) == 5) || (strlen(r) == 5))
		{
			char tmp1[80];
			char tmp2[80];
			int i,j;

			const char *t1 = l;
			const char *t2 = r;


			i = 0; while(*t1 && i < 10) { if(isdigit(*t1)) tmp1[i++] = *t1; t1++; }; tmp1[i] = 0;
			j = 0; while(*t2 && j < 10) { if(isdigit(*t2)) tmp2[j++] = *t2; t2++; }; tmp2[j] = 0;

			if(mars.debug)
				marslog(LOG_DBUG,"eq_date: %s(%s) %s %s(%s)",l , tmp1,
					(strcmp(tmp1+i-4,tmp2+j-4)==0)?" == ":" != ",r,tmp2);

			return (strcmp(tmp1+i-4,tmp2+j-4) == 0);
		}
#endif

		lndate = name_to_date(l,false,&isjul);
		rndate = name_to_date(r,false,&isjul);


		if(mars.debug)
			marslog(LOG_DBUG,"eq_date: %ld %s %ld",lndate,lndate==rndate?" == ":" != ",rndate);

		return lndate==rndate;
	}
	return false;
}

boolean eq_time(const char *l, const char *r)
{
	if(l && r)
		return atol(l) == atol(r);
	return false;
}

boolean eq_default(const char *l, const char *r)
{
	boolean ok = false;
	/* printf("eq_default %s %s\n",l,r); */

	if(!l || !r)
		return false;

	if(is_number(r))
	{
		if(atof(l) == atof(r))
			ok = true;
	}
	else
		if(EQ(l,r))
			ok = true;
	return ok;
}


/* Keep in sync with 'names' below ... */

#define DATE_INDIX	0
#define TIME_INDIX	2
#define STEP_INDIX	4

/* Used, among other things (??), for field ordering
   in a fieldset */
static char *names[] = {

	"DATE",
	"HDATE",
	"TIME",

	"REFERENCE",
	"STEP",
	"ANOFFSET",
	"VERIFY",
	"FCMONTH",
	"FCPERIOD",
	"LEADTIME",
	"OPTTIME",

	"EXPVER",
	"ORIGIN",
	"DOMAIN",
	"METHOD",

	"DIAGNOSTIC",
	"ITERATION",

	"NUMBER",
	"QUANTILE",

	"LEVELIST",
	"LATITUDE",
	"LONGITUDE",
	"RANGE",

	"PARAM",

	"IDENT",
	"OBSTYPE",
    "INSTRUMENT",
	"REPORTYPE",

	 /* For 2-d wave-spectra products */
	"FREQUENCY",
	"DIRECTION",

	/* For EA, EF */
	"CHANNEL",

};

char** mars_order()
{
	return names;
}

int mars_order_count()
{
	return NUMBER(names);
}

static namecmp cmpnames[] = {
	eq_default,   /* DATE */
	eq_default,   /* HDATE */
	eq_default,   /* TIME */
	eq_default,   /* REFERENCE */

	eq_range,     /* STEP */
	eq_range,     /* ANOFFSET */
	eq_default,   /* VERIFY */
	eq_default,   /* FCMONTH */
	eq_range,     /* FCPERIOD: It's like step range */
	eq_range,     /* LEADTIME */
	eq_range,     /* OPTTIME */

	eq_default,   /* EXPVER */

	eq_string,    /* ORIGIN */
	eq_string,    /* DOMAIN */
	eq_integer,   /* METHOD */
	eq_integer,   /* DIAGNOSTIC */
	eq_integer,   /* ITERATION */

	eq_integer,   /* NUMBER */
	eq_range,     /* QUANTILE */

	eq_real,      /* LEVELIST */
	eq_real,      /* LATITUDE */
	eq_real,      /* LONGITUDE */
	eq_default,   /* RANGE */

	eq_param,     /* PARAM */

	eq_integer,   /* IDENT */
	eq_integer,   /* OBSTYPE */
	eq_integer,   /* INSTRUMENT */
	eq_integer,   /* REPORTYPE */

	eq_integer,   /* FREQUENCY */
	eq_integer,   /* DIRECTION */

	eq_integer,   /* CHANNEL */
};

static char *names2[] = {

	"CLASS",
	"STREAM",
	"TYPE",
	"LEVTYPE",
	"PRODUCT",
	"SECTION",
};

static namecmp cmpnames2[] = {
	eq_string, /* CLASS */
	eq_string, /* STREAM */
	eq_string, /* TYPE */
	eq_string, /* LEVTYPE */
	eq_string, /* PRODUCT */
	eq_string, /* SECTION */
};


namecmp compare_parameters(const char *name)
{
	int i = 0;
	for(i = 0 ; i < NUMBER(cmpnames) ; i++)
		if(EQ(name,names[i]))
			return cmpnames[i];

	for(i = 0 ; i < NUMBER(cmpnames2) ; i++)
		if(EQ(name,names2[i]))
			return cmpnames2[i];

	marslog(LOG_WARN,"No comparator for %s",name);
	return eq_string;
}


#define CASEEQ(a,b) (strcasecmp(a,b)==0)

static const char *parse1(const char *p,int* x,int *n)
{
	*x = *n = 0;
	while(*p && isdigit(*p))
	{
		(*x) *= 10;
		(*x) += *p - '0';
		(*n)++;
		p++;
	}
	return p;
}


boolean parsetime(const char *name,int* h,int *m,int* s)
{
	const char *p = name;
	int n;
	int H=0,M=0,S=0;

	/* hour */

	p = parse1(p,&H,&n);
	if(n != 0)
	{
		if(n != 2) return false;

		/* minute */
		if(*p++ != ':') return false;

		p = parse1(p,&M,&n);
		if(n != 2) return false;

		if(*p != 0)
		{
			/* second */

			if(*p++ != ':') return false;
			p = parse1(p,&S,&n);
			if(n != 2) return false;
		}
	}

	*h = H;
	*m = M;
	*s = S;

	return *p == 0 ? true : false;
}

boolean parsedate(const char *name,long* julian,long *second,boolean* isjul)
{
	const char *p = name;
	int n;
	int y=0,m=0,d=0,H=0,M=0,S=0;

	*julian = *second = 0;
	*isjul = false;

	if(p == 0 || *p == 0) return false;

	/* year */
	p = parse1(p,&y,&n);
	if(n != 2 && n != 4) return false;
	if(*p++ != '-') return false;

	/* month */
	p = parse1(p,&m,&n);
	if(n == 2)
	{
		/* day */
		if(*p++ != '-') return false;
		p = parse1(p,&d,&n);
		if(n != 2) return false;
	}
	else if(n == 3)
	{
	    long j = mars_date_to_julian(y*10000 + 101) + m - 1;
		j = mars_julian_to_date(j,mars.y2k && !mars.daily_climatology);
		/* julian day */;
		d = j % 100;
		m = (j % 10000)/100;
		*isjul = true;
	}
	else
		return false;

	while(*p && isspace(*p)) p++;

	/* hour */

	p = parse1(p,&H,&n);
	if(n != 0)
	{
		if(n != 2) return false;

		/* minute */
		if(*p++ != ':') return false;

		p = parse1(p,&M,&n);
		if(n != 2) return false;

		if(*p != 0)
		{
			/* second */

			if(*p++ != ':') return false;
			p = parse1(p,&S,&n);
			if(n != 2) return false;
		}
	}

	*julian = mars_date_to_julian(y*10000 + m*100 + d);
	*second = H*3600 + M*60 + S;

	return *p == 0 ? true : false;

}

boolean isdate(const char *name)
{
	long dummy1,dummy2;
	boolean dummy3;
	return parsedate(name,&dummy1,&dummy2,&dummy3);
}

boolean istime(const char *name)
{
	int h,m, s;
	return parsetime(name,&h,&m,&s);
}

boolean is_integer(const char *name)
{
	const char *p = name;
	int x,n;

	if(p == 0 || *p == 0) return false;

	if(*p == '-') p++;
	else if(*p == '+') p++;

	p = parse1(p,&x,&n);
	if(*p) return false;

	return true;
}

boolean is_number(const char *name)
{
	const char *p = name;
	int x,n;

	if(p == 0 || *p == 0) return false;

	if(*p == '-') p++;
	else if(*p == '+') p++;

	p = parse1(p,&x,&n); if(n==0 && *p != '.') return false;

	if(*p == '.') {
		p++;
		p = parse1(p,&x,&n);
	}

	if(*p == 'e' || *p == 'E')
	{
		p++;
		if(*p == '-') p++;
		else if(*p == '+') p++;
		p = parse1(p,&x,&n); if(n==0) return false;
	}

	return *p == 0 ? true : false;
}

boolean isrange(const char *name)
{
	const char *p = name;
	int x,n;

	if(p == 0 || *p == 0) return false;

	p = parse1(p,&x,&n); if(n==0) return false;

	if(*p != '-' && *p != ':') return false;

	p++;
	p = parse1(p,&x,&n);

	return *p == 0 ? true : false;
}

static void count_parval(parameter *p)
{
	int n = 0;
	value *v = p->values;

	while(v)
	{
		n++;
		v = v->next;
	}

	p->count = n;

}

long mars_julian_to_date(long jdate,boolean century)
{
	long x,y,d,m,e;
	long day,month,year;

	x = 4 * jdate - 6884477;
	y = (x / 146097) * 100;
	e = x % 146097;
	d = e / 4;

	x = 4 * d + 3;
	y = (x / 1461) + y;
	e = x % 1461;
	d = e / 4 + 1;

	x = 5 * d - 3;
	m = x / 153 + 1;
	e = x % 153;
	d = e / 5 + 1;

	if( m < 11 )
		month = m + 2;
	else
		month = m - 10;


	day = d;
	year = y + m / 11;

	if(century)
		return year * 10000 + month * 100 + day;
	else
		return (year % 100) * 10000 + month * 100 + day;
}

long today(void)
{
	static long td = 0;
	if(td == 0)
	{
		time_t now;
		struct tm *t;

		time(&now);

		t = localtime(&now);

		td = (t->tm_year+1900) * 10000 + (t->tm_mon+1)* 100 + t->tm_mday;
		td = mars_date_to_julian(td);

	}
	return td;
}

long mars_date_to_julian(long ddate)
{
	long  m1,y1,a,b,c,d,j1;

	long month,day,year;

	if(ddate <=0)
	{
		const char *ref = getenv("MARS_REFERENCE_DATE");
		if(ref) {
			long date = atol(ref);
			date = mars_julian_to_date(mars_date_to_julian(date),mars.y2k);
			marslog(LOG_WARN,"Using reference date %d",date);
			return mars_date_to_julian(date) + ddate;
		}
		return today() + ddate;
	}

	year = ddate / 10000;
	ddate %= 10000;
	month  = ddate / 100;
	ddate %= 100;
	day = ddate;


	if (year < 100)
	{
		static int first = 1;
		if(first)
		{
			if(mars.y2k)
			{
				marslog(LOG_WARN,"** Y2K ALERT ** Dates should be specified with a four digits year");
				marslog(LOG_WARN,"** Y2K ALERT ** ");
				marslog(LOG_WARN,"** Y2K ALERT ** Use of two digit year format will be illegal in MARS requests.");
				marslog(LOG_WARN,"** Y2K ALERT ** For more details see Computer News Sheet Number 359.");
				marslog(LOG_WARN,"** Y2K ALERT ** ");

				mars.y2k_problem = year;
				if(!mars.y2k_problem)
					mars.y2k_problem = -1;

				first = 0;
			}

		}
		year = year + 1900;
	}

	if (month > 2)
	{
		m1 = month - 3;
		y1 = year;
	}
	else
	{
		m1 = month + 9;
		y1 = year - 1;
	}
	a = 146097*(y1/100)/4;
	d = y1 % 100;
	b = 1461*d/4;
	c = (153*m1+2)/5+day+1721119;
	j1 = a+b+c;

	return(j1);
}

static char * months[] = {
"jan", "feb", "mar", "apr", "may", "jun",
"jul", "aug", "sep", "oct", "nov", "dec",
};

static boolean get_date_parts(const char* name,int *year, int* month, int* day)
{
	int i;
	char tmp[80];

	*year = *month = *day;

	int l = strlen(name);
	switch(l) {
		case 3:
			for(i = 0; i < NUMBER(months); i++)
				if(strcasecmp(name, months[i]) == 0)
				{
					*month = i + 1;
					return true;
				}
			break;

		case 6:
			strncpy(tmp,name,3); tmp[3] = 0;
			for(i = 0; i < NUMBER(months); i++)
				if(strcasecmp(tmp, months[i]) == 0)
				{
					if(name[3] == '-') {
						*day   = atol(&name[4]);
						*month = i + 1;
						return true;
					}
				}
			break;

		default:
			break;
	}
	return false;
}


static long name_to_date(const char *name, boolean by,boolean* isjul)
{
	long julian,second, i, tmp[4];
	if(by)
		return atol(name);


	if(parsedate(name,&julian,&second,isjul))
	{
		if(second)
			marslog(LOG_WARN,"HH:MM:SS Information lost from %s",name);
		return julian;
	}
	return mars_date_to_julian(atol(name));
}

/* this is too specific... I'd like to change it later */

static long name_to_time(const char *name, boolean by)
{
	long t = atol(name);
	return (t/100)*60+(t%100);
}

static long name_to_int(const char *p,char *name, boolean by,boolean* isjul)
{
	if(EQ(p,"DATE"))
		return name_to_date(name,by,isjul);
	else if(EQ(p,"TIME"))
		return name_to_time(name,by);
	else
		return atol(name);
}

static char *int_to_name(const char *p,int n,boolean isjul)
{
	static char buf[20];
	char *fmt = "%d";
	int k = 0;

	if(EQ(p,"DATE")) {
		if(isjul)
		{
			long year  = mars_julian_to_date(n,mars.y2k) / 10000;
			long delta = n - mars_date_to_julian(year*10000+101);
			n = year;
			k = delta+1;
			fmt = "%d-%03d";

		}
		else n = mars_julian_to_date(n,mars.y2k);
	}
	else if(EQ(p,"TIME"))
	{
		int h = n/60;
		int m = n%60;
		n = h*100+m;
		fmt = "%04d";
	}
	sprintf(buf,fmt,n,k);
	return buf;
}

void check_for_to_by_list(parameter *p)
{
	value *q[5];
	int i = 0;
	value *v = p->values;
	int from,to,by;
	int doit;
	boolean isjul = false;



	while(v)
	{
		q[i++] = v;
		v = v->next;
	}

	if(p->count == 5)
	{
		doit = EQ(q[1]->name,"TO") &&
			   EQ(q[3]->name,"BY") &&
			   (is_number(q[0]->name) || isdate(q[0]->name)) &&
			   (is_number(q[2]->name) || isdate(q[2]->name)) &&
			   is_number(q[4]->name);

		if(doit)
		{

			from = name_to_int(p->name,q[0]->name,false,&isjul);
			to   = name_to_int(p->name,q[2]->name,false,&isjul);
			by   = name_to_int(p->name,q[4]->name,true,&isjul);
		}

	}
	else
	{
		doit = EQ(q[1]->name,"TO") &&
			(is_number(q[0]->name) || isdate(q[0]->name)) &&
			(is_number(q[2]->name) || isdate(q[2]->name));

		if(doit)
		{

			from = name_to_int(p->name,q[0]->name,false,&isjul);
			to   = name_to_int(p->name,q[2]->name,false,&isjul);
			by = 1;
			if(EQ(p->name,"DATE")) by = 1;
			if(EQ(p->name,"STEP")) by = 12;
			if(EQ(p->name,"TIME")) by = 6*60;
		}
	}

	if( doit )
	{
		int n    = 0;
		value  *f = NULL,*l = NULL;
		long val;
		long cnt;

		if(by == 0) by = 1;

		if( (to - from) / (double)by < 0 )
		{
			marslog(LOG_WARN,"Expanding list of %s in reverse order",p->name);
			by = -by;
		}

		cnt =  (to - from)/by + 1;

		val = from;
		for(i=0 ; i < cnt ; i++)
		{
			v = new_value(strcache(int_to_name(p->name,val,isjul)));
			val += by;


			if(f) l->next = v;
			else f = v;
			l = v;
			n++;
		}

		if(n>99)
		{
			/* due to a bug on the ibm, save the original stuff here */
			value *w;

			w = v = new_value(strcache(int_to_name(p->name,from,isjul)));
			v = (v->next = new_value(strcache("BY")));
			v = (v->next = new_value(strcache(int_to_name("DUMMY",by,isjul))));
			v = (v->next = new_value(strcache("TO")));
			v = (v->next = new_value(strcache(int_to_name(p->name,to,isjul))));

			p->ibm_values = w;
		}

		free_all_values(p->values);
		p->values  = f;
		p->count = n;

	}

}

long get_julian_from_request(const request* req,int i)
{
	long req_date;
	const char* p = get_value(req,"DATE",i);
	if(is_number(p))
		req_date = mars_date_to_julian(atol(p));
	else
	{
		long second = 0; boolean isjul;
		parsedate(p,&req_date,&second,&isjul);
	}

	return req_date;
}

boolean fetch(const request *r)
{
	const char* p = get_value(r,"TRANSFER",0);
	if(p && EQ(p,"HANDLE"))
		return true;

	return EQ(r->name,"FETCH");
}

boolean observation(const request *r)
{
	const char *t = get_value(r,"TYPE",0);
	const char *s = get_value(r,"STREAM",0);
	return s && t && (EQ(t,"OB") || EQ(t,"FB") || EQ(s,"SSMI"));
}

boolean image(const request *r)
{
	const char *t = get_value(r,"TYPE",0);
	const char *s = get_value(r,"STREAM",0);
	return s && t && (EQ(t,"IM")) && !EQ(s,"SSMI");
}

boolean simulated_image(const request *r)
{
	const char *t = get_value(r,"TYPE",0);
	return t && (EQ(t,"SIM"));
}

boolean gridded_observations(const request *r)
{
	const char *t = get_value(r,"TYPE",0);
	return t && (EQ(t,"GO"));
}

boolean feedback(const request *r)
{
	const char *t = get_value(r,"TYPE",0);
	return t && (EQ(t,"AI") || EQ(t,"AF"));
}

boolean bias(const request *r)
{
	const char *t = get_value(r,"TYPE",0);
	return t && (EQ(t,"AB"));
}

boolean track(const request *r)
{
	const char *t = get_value(r,"TYPE",0);
	return t && (EQ(t,"TF"));
}

boolean is_bufr(const request* r)
{
	return feedback(r) || observation(r) || bias(r) || track(r);
}

boolean is_odb(const request* r)
{
    const char *t = get_value(r,"TYPE",0);
    return t && (EQ(t,"OFB") || EQ(t,"MFB") || EQ(t,"OAI") || EQ(t,"SFB") || EQ(t,"FSOIFB") || EQ(t,"FCDFB"));
}

boolean is_ocean(const request* r)
{
    const char *t = get_value(r,"LEVTYPE",0);
    return t && (EQ(t,"O2D") || EQ(t,"O3D"));
}

boolean is_grib(const request* r)
{
	return !is_bufr(r);
}

boolean wave2d(const request *r)
{
	const char *s;
	const char *t = get_value(r,"STREAM",0);
	boolean is250 = false;
	int n = 0;

	if(t && !EQ(t,"WAVE"))
		return false;

	while((s = get_value(r,"PARAM",n++)) != NULL)
		if((EQ(s,"250")) || (EQ(s,"250.141")) || (EQ(s,"ALL")))
			is250 = true;

	return is250;
}

/* end of specific code
=====================*/


parameter *find_parameter(const request *r,const char *parname)
{
	if(!parname) return 0;
	if(r)
	{
		parameter *p = r->params;
		while(p)
		{
			if(EQ(p->name,parname))
				return p;
			p = p->next;
		}
	}
	return NULL;

}

parameter *find_case_parameter(const request *r,const char *parname)
{
	if(!parname) return 0;
	if(r)
	{
		parameter *p = r->params;
		while(p)
		{
			if(CASEEQ(p->name,parname)==0)
				return p;
			p = p->next;
		}
	}
	return NULL;

}

boolean value_exist(const request *r, const char *param, const char *val)
{
	boolean found=false;
	value *v;
	boolean num = is_number(val);
	double dval = atof(val);

	char *vcache = strcache(val);

	parameter *p = find_parameter(r,param);
	if(!p) return false;

	v = p->values;

	while(v && !found)
	{
		if(num)
			found = (dval == atof(v->name));
		else
			found = (vcache == v->name);
		v = v->next;
	}
	strfree(vcache);

	return found;
}

int count_values(const request *r,const char *parname)
{
	parameter *p = find_parameter(r,parname);

	if(p == NULL) return 0;
	if(p->count)  return p->count;

	count_parval(p);

	if(mars.expflags & EXPAND_LISTS)
	{
		if(p->count == 5 || p->count == 3)
			/* small patch */
			if(!EQ(parname,"TIME")  || !(observation(r)))
				check_for_to_by_list(p);
	}

	if((mars.expflags & EXPAND_DATE) && (EQ(parname,"DATE")))
	{
		value *v = p->values;
		while(v)
		{
			check_for_date(p,v);
			v = v->next;
		}
	}

	if((mars.expflags & EXPAND_TIME) && (EQ(parname,"TIME")))
	{
		value *v = p->values;
		while(v)
		{
			check_for_time(p,v);
			v = v->next;
		}
	}

	return p->count;
}


const char *get_value(const request *r,const char *parname,int nth)
{
	parameter *p = find_parameter(r,parname);
	value *v;
	int i = 0;

	if(p == NULL) return NULL;

	if(!p->count) count_values(r,parname);

	v = p->values;

	while(v)
	{
		if(nth == i++) return v->name;
		v = v->next;
	}

	return NULL;
}

const char *case_get_param(const request *r,const char *parname)
{
	parameter *p = find_case_parameter(r,parname);
	return p?p->name:NULL;
}

const char *case_get_value(const request *r,const char *parname,int nth)
{
	parameter *p = find_case_parameter(r,parname);
	return p?get_value(r,p->name,nth):NULL;
}

int case_count_values(const request *r,const char *parname,int nth)
{
	parameter *p = find_case_parameter(r,parname);
	return p?count_values(r,p->name):0;
}

const char *get_operator(const request *r,const char *parname,int nth)
{

	return NULL;
}

static void put_value(request *r,const char *parname,const char *valname,
boolean append,boolean unique, boolean ascending)
{
	parameter *p;
	value     *v;

	if(!r) return;


	if( (p = find_parameter(r,parname)) != NULL )
	{
		if(append)
		{
			value *a = p->values,*b= NULL,*c=NULL;
			while(a)
			{
				b = a;
				if(unique)
				{
					if(is_number(a->name) && is_number(valname))
					{
						if(atof(a->name) == atof(valname))
							return;
					}
					else if(EQ(a->name,valname))
						return;
				}

				if(ascending)
				{
					if(is_number(a->name))
					{
						if(atof(valname)  < atof(a->name))
							break;
					}
					else if(LT(valname,a->name))
						break;
				}
				c = b;
				a = a->next;
			}
			v = new_value(strcache(valname));
			if(ascending)
			{
				if(c)
				{
					if(b && b!=c) v->next = b;
					c->next = v;
				}
				else
				{
					if(a) v->next = a;
					p->values = v;
				}
			}
			else
			{
				if(b) b->next = v;
				else p->values = v;
			}
			/* p->count++; */
			p->count = 0;
		}
		else
		{
			if(p->values)
			{
				free_all_values(p->values->next);
				p->values->next = NULL;
				/* p->count = 1; */
				p->count = 0;
				if(EQ(p->values->name,valname))
					return;
				else
				{
					strfree(p->values->name);
					p->values->name = strcache(valname);
				}
			}
			else
			{
				v = new_value(strcache(valname));
				p->values = v;
				/* p->count = 1; */
				p->count = 0;
			}
		}
	}
	else
	{
		parameter *q = NULL;
		parameter *s = r->params;
		v = new_value(strcache(valname));
		p = new_parameter(strcache(parname),v);
		while(s) {
			q = s;
			s=s->next;
		}
		if(q) q->next = p;
		else r->params = p;
	}

}

void unset_value(request *r,const char *parname)
{
	parameter *p,*q=NULL;
	if(!r) return;

	p = r->params;
	while(p)
	{
		if(EQ(parname,p->name))
		{
			if(q) q->next = p->next;
			else r->params = p->next;
			free_one_parameter(p);
			return;
		}
		q = p;
		p = p->next;
	}
}

void set_value(request *r,const char *parname,const char *fmt,...)
{
	char buffer[10240];
	va_list list;

	va_start(list,fmt);
	vsprintf(buffer,fmt,list);
	va_end(list);

	put_value(r,parname,buffer,false,false,false);
}

void set_value_int(request *r, const char *p, long64 v)
{
	set_value(r,p,"%lld",v);
}

void add_value_int(request *r, const char *p, long64 v)
{
	add_value(r,p,"%lld",v);
}

void set_list(request *r,const char *parname,const char *plist)
{
	const char *p = plist;
	int i = 0,space = 0;
	char buf[1024];
	boolean append = false;

	while(*p)
	{
		switch(*p)
		{
		case '\t':
		case ' ':
			space++;
			break;

		case '/':
			if(i)
			{
				buf[i] = 0;
				put_value(r,parname,buf,append,false,false);
			}
			space = i = 0;
			append= true;
			break;

		default:
			if(i && space) while(space-->0) buf[i++] = ' ';
			buf[i++] = *p;
			space = 0;
			break;
		}
		p++;
	}

	if(i)
	{
		buf[i] = 0;
		put_value(r,parname,buf,append,false,false);
	}
}

void add_unique_value(request *r,const char *parname,const char *fmt,...)
{
	char buffer[1024];
	va_list list;

	va_start(list,fmt);
	vsprintf(buffer,fmt,list);
	va_end(list);

	put_value(r,parname,buffer,true,true,false);
	va_end(list);
}

void add_ordered_value(request *r,const char *parname,const char *fmt,...)
{
	char buffer[1024];
	va_list list;

	va_start(list,fmt);
	vsprintf(buffer,fmt,list);
	va_end(list);

	put_value(r,parname,buffer,true,false,true);
	va_end(list);
}

void add_unique_ordered_value(request *r,const char *parname,const char *fmt,...)
{
	char buffer[1024];
	va_list list;

	va_start(list,fmt);
	vsprintf(buffer,fmt,list);
	va_end(list);

	put_value(r,parname,buffer,true,true,true);
	va_end(list);
}

void add_value(request *r,const char *parname,const char *fmt,...)
{
	char buffer[1024];
	va_list list;

	va_start(list,fmt);
	vsprintf(buffer,fmt,list);
	va_end(list);

	put_value(r,parname,buffer,true,false,false);
	va_end(list);
}

void set_value_long64(request *r,const char *parname,long64 l)
{
	char buffer[1024];
	long64 i;

	sprintf(buffer,"%lld",l);
	sscanf(buffer,"%lld",&i);

	/* i = strtoll(buffer,(char **)NULL, 10); */
	if (i != l)
	{
		marslog(LOG_EROR,"Error in converting 64 bit long to ascii");
		marsexit(-99);
	}

	put_value(r,parname,buffer,false,false,false);
}

void add_value_long64(request *r,const char *parname,long64 l)
{
	char buffer[1024];
	long64 i;

	sprintf(buffer,"%lld",l);
	sscanf(buffer,"%lld",&i);

	/* i = strtoll(buffer,(char **)NULL, 10); */
	if (i != l)
	{
		marslog(LOG_EROR,"Error in converting 64 bit long to ascii");
		marsexit(-99);
	}

	put_value(r,parname,buffer,true,false,false);

}

const char *request_verb(const request *r)
{
	return r?r->name:NULL;
}

/*
	Parameter 127 && 128 && 152 are only at level 1 !!!!
	129 is at level 1
*/

static boolean chk_152(int count,char *names[],char *vals[])
{
	int flg = 0;
	int i,n;

	for(i=0;i<count;i++)
	{
		if((EQ(names[i],"PARAM")) && vals[i] && ( n = atoi(vals[i])) )
		{
			if(n == 22 || n == 127 || n == 128 || n == 129 || n == 152 || n == 200152 || n == 200129 || n == 129152 || n == 129129)
			{
				/* Only for tables < 210 */
				long par = NO_PARAM;
				long table = NO_TABLE;
				paramtable(vals[i],&par,&table,true);
				if(table <210)
					flg++;
			}
		}

		if(EQ(names[i],"LEVELIST") && vals[i] && !(EQ(vals[i],"1") || EQ(vals[i],"01")))
			flg++;
	}

	return (boolean)(flg != 2);
}

static void loop(const request *r,boolean ml,int index,int count,char *strings[],
char *values[],
loopproc callback,void *data)
{
	if(index < count)
	{
		parameter  *p = find_parameter(r,strings[index]);

		(void)count_values(r,strings[index]); /* force list expension */

		if(p)
		{
			value      *v = p->values;

			while(v)
			{
				values[index] = v->name;
				loop(r,ml,index+1,count,strings,values,callback,data);
				v = v->next;
			}
		}
		else
		{
			values[index] = NULL;
			loop(r,ml,index+1,count,strings,values,callback,data);
		}

	}
	else
		if( !ml || chk_152(count,strings,values))
			callback(r,count,strings,values,data);
}

void values_loop(const request *r,int count,
char *parnames[],loopproc callback,void *data)
{
	char **values = (char**)MALLOC(sizeof(char*)*count);
	const char * p      = get_value(r,"LEVTYPE",0);
	boolean ml    = (boolean)(p && (EQ(p,"ML")));

	if(ml) {
		p = get_value(r,"EXPECT",0);
		if(p && atol(p) != 0)
		{
			marslog(LOG_WARN,"EXPECT provided, special treatment of LNSP");
			marslog(LOG_WARN,"and other single level parameters disabled");
			ml = false;
		}
	}

	loop(r,ml,0,count,parnames,values,callback,data);

	FREE(values);
}



request *new_request(char *name,parameter *p)
{
	request *r = NEW_CLEAR(request);
	r->name    = name;
	r->params  = p;
	return r;
}

request *empty_request(const char *name)
{
	return new_request(name?strcache(name):strcache(""),NULL);
}

parameter *new_parameter(char *name,value *v)
{
	parameter *p = NEW_CLEAR(parameter);
	p->name    = name;
	p->values  = v;
	return p;
}

value *new_value(char *name)
{
	value *v = NEW_CLEAR(value);
	v->name  = name;
	return v;
}


value *new_expansion(value* val)
{
	value *v  = NEW_CLEAR(value);
	v->expand = val;
	return v;
}

value *new_reference(const char *refname,char *name)
{
	value *v = NEW_CLEAR(value);
	v->ref   = (value*)refname;
	v->name  = name;
	return v;
}


static boolean need_quotes(const char *p)
{
	int n;
	const char *q;

	switch(*p)
	{
		case '-':
		case '.':
			return !is_number(p);
			/*NOTREACHED*/
			break;

		case '"':
			n = 0;
			q = p;
			while(*q) if(*q++ == '"') n++;
			if(n != 2) return true;
			if(p[strlen(p)-1] != '"') return true;
			break;

		default:
			if(!isalnum(*p) && *p != '_')
				return true;

			while(*p)
			{
				if(!isalnum(*p) &&
				   *p != ' ' &&
				   *p != '_' &&
				   *p != '-' &&
				   *p != '.' &&
				   *p != ':' &&
				   *p != '\t') return true;

				 p++;
			}

			break;
	}

	return false;
}

static void save_name(FILE *f,const char* name,int n)
{
	const char *p = name;
	int i = 0,cnt = 0;

	if(name == NULL)
	{
		marslog(LOG_WARN,"save_name called with empty name");
		return;
	}

	if(need_quotes(p))
	{
		cnt = 2;
		putc('\'',f);
		while(*p) {
			if(*p=='\\' || *p == '\n' || *p == '\'') { putc('\\',f);i++; }
			putc(*p,f);
			p++;
			i++;
		}
		putc('\'',f);
	}
	else cnt = fprintf(f,"%s",name);

	for(i=cnt;i<n;i++)
		putc(' ',f);
}

static void save_one_value(FILE *f,value *r)
{
	save_name(f,r->name,0);
}

static void save_all_values(FILE *f,value *r)
{
	while(r)
	{
		save_one_value(f,r);
		if(r->next) putc('/',f);
		r = r->next;
	}

#if 0
	value *o = r->other_names;
	if(o)
	{
		fprintf(f," ## Other names: ");
		while(o)
		{
			save_one_value(f,o);
			if(o->next) putc('/',f);
			o = o->next;
		}
	}
#endif
}


static void save_all_parameters(FILE *f,parameter *r)
{
	while(r)
	{
		if(mars.debug || *r->name != '_')
		{
			if(r->subrequest)
			{
				fprintf(f,",\n    ");
				save_name(f,r->name,10);
				fprintf(f," = (");
				save_all_requests(f,r->subrequest);
				fprintf(f,")");
			}
			else
				if(r->values)
				{
					fprintf(f,",\n    ");
					save_name(f,r->name,10);
					fprintf(f," = ");
					save_all_values(f,r->values);
				}
#if 0
			if(r->default_values)
			{
				putc('\n',f);
				putc('#',f);
				save_all_values(f,r->default_values);
				putc('\n',f);

			}
#endif
		}
		r = r->next;
	}
	putc('\n',f);
}


void save_one_request(FILE *f,const request *r)
{
	if(r)
	{
		save_name(f,r->name,0);
		save_all_parameters(f,r->params);
		putc('\n',f);
	}
}


void save_all_requests(FILE *f,const request *r)
{
	while(r)
	{
		save_one_request(f,r);
		r = r->next;
	}
}

void print_one_request(const request *r)
{
	save_one_request(stdout,r);
}

void print_all_requests(const request *r)
{
	save_all_requests(stdout,r);
}

request *read_request_file(const char *fname)
{
	extern request *parser_reqs;
	request *r;

	if(parser(fname,mars.echo) != NOERR)
	{
		free_all_requests(parser_reqs);
		parser_reqs = NULL;
		return NULL;
	}
	else
	{
		r = parser_reqs;
		parser_reqs = NULL;
		return r;
	}
}


static request *first;
static request *last;

static void reqcb(const request *r,int count,char *names[],
char *vals[],void *data)
{
	request *w = clone_one_request(r);
	int     i;
	/*request **d = (request**)data;*/

	int *n = (int*)data;

	w->order = (*n)++;
	for(i=0;i<count;i++)
	{
		/* printf("%s = %s\n",names[i],vals[i]); */
		if(vals[i])
			put_value(w,names[i],vals[i],false,false,false);
	}

	if(first == NULL)
		first = w;
	else
		last->next = w;
	last = w;
}

request *unwind_one_request(const request *r)
{

	int n = 0;
	first = last = NULL;
	names_loop(r,reqcb,&n);
	return first;
}

request *custom_unwind_one_request(const request *r,int cnt,char *names[])
{
	int n = 0;
	first = last = NULL;
	values_loop(r,cnt,names,reqcb,&n);
	return first;
}

void names_loop(const request *r,loopproc proc,void *data)
{
	if(NUMBER(names) != NUMBER(cmpnames))
	{
		marslog(LOG_EROR,"MARS internal error in request.c");
		marslog(LOG_EROR,"NUMBER(names) [%d] != NUMBER(cmpnames) [%d]",NUMBER(names),NUMBER(cmpnames));
		marslog(LOG_EROR,"Exiting...");
		marsexit(1);
	}
	values_loop(r,NUMBER(names),names,proc,data);
}

static void cntcb(const request *r,int count,char *names[],char *vals[],void *data)
{
	int *n = (int*)data;
	(*n)++;
}

boolean all_is_used(const request *r)
{
	static const request *r_keep  = 0;
	static       boolean     all     = false;
	       const char    *s       = 0;
	             int      i       = 0;

	if(r_keep == r)
		return all;
	r_keep = r;

	for(i=0;i<NUMBER(names);i++)
		if((s = get_value(r,names[i],0)) != NULL)
			if(EQ(s,"ALL"))
			{
				all = true;
				break;
			}

	return all;
}

int count_fields(request *r)
{
	int  n = 0;
	const char *s = get_value(r,"EXPECT",0);

	if(s != NULL)
		return atol(s);

	if(is_bufr(r) || image(r) ) return 0;

	if(fetch(r))
		return 0;

	if(all_is_used(r))
		return 0;

    if (is_odb(r))
        return 0;
/*
    if (is_ocean(r))
        return 0;
*/
	names_loop(r,cntcb,&n);

	return n;
}

int count_fields_in_request(request *r)
{
	int  n = 0;

	if(is_bufr(r) || image(r) ) return 0;

	if(fetch(r))
		return 0;

	if(all_is_used(r))
		return 0;

	if (is_odb(r))
		return 0;

	names_loop(r,cntcb,&n);

	return n;
}


int reqcmp(const request *a,const request *b,boolean verbose)
{
	int n;
	if(a && b)
	{
		parameter *p = a->params;

		while(p)
		{
			const char *s = get_value(a,p->name,0);
			const char *t = get_value(b,p->name,0);

			if(s && t)
			{
				if(is_number(s))
					n = atof(s) - atof(t);
				else
					n = strcmp(s,t);

				if(n) {
					if(verbose)
						marslog(LOG_INFO,
						    "Compare failed: %s -> %s <> %s",p->name,s,t);
					return n;
				}
			}

			p = p->next;
		}
	}
	return 0;
}

typedef struct {
	const   request *r;
	int     order;
	int     cnt;
	int     fguess;
	int     fcast;
} s_order;

static void ordercb(const request *r,int count,char *names[],
char *vals[],void *data)
{
	s_order *o = (s_order *)data;
	char buf[10];
	char buf2[10];
	int i;
	const char *t;
	char *s;
	boolean ok = true;

	for(i=0;i<count && ok;i++)
		if((s = vals[i]) && (t = get_value(o->r,names[i],0)))
		{
			if(o->fguess && o->fcast)
			{
				boolean isjul = false;
				int date = name_to_date(vals[DATE_INDIX],false,&isjul);
				int time = atol(vals[TIME_INDIX])/ 100;
				int step = atol(vals[STEP_INDIX]);

				if(step == 0) step = 6;
				time -= step;

				while(time < 0)
				{
					time += 24;
					date -= 1;
				}

				if(EQ(names[i],"DATE"))
				{

					long n = name_to_date(t,false,&isjul);
					sprintf(buf2,"%ld",mars_julian_to_date(n,mars.y2k));
					t = buf2;

					sprintf(buf,"%ld",mars_julian_to_date(date,mars.y2k));
					s = buf;
				}

				if(EQ(names[i],"TIME"))
				{
					sprintf(buf,"%d",time*100);
					s = buf;
				}

				if(EQ(names[i],"STEP"))
				{
					sprintf(buf,"%d",step);
					s = buf;
				}

			}

			if(!cmpnames[i](s,t))
				ok = false;

			if(!ok && mars.debug)
				marslog(LOG_DBUG,"%s different %s <> %s",names[i],s,t);
		}


	if(ok) o->order = o->cnt;

	o->cnt++;

}

int field_order(const request *r,const request *u)
{
	s_order o;
	const char    *kindr = get_value(r,"TYPE",0);
	const char    *kindu = get_value(u,"TYPE",0);

	o.r      = u;
	o.order  = -1;
	o.cnt    = 0;
	o.fguess = kindr?strcmp(kindr,"FG")==0:false;
	o.fcast  = kindu?strcmp(kindu,"FC")==0:false;

	names_loop(r,ordercb,&o);

	if(mars.debug) {
		marslog(LOG_DBUG,"Order : %d",o.order);
		print_all_requests(r);
		print_all_requests(u);
	}

	return o.order;
}

void valcpy(request *a,request *b,char *aname,char *bname)
{
	parameter *p;
	if(a && b && (p = find_parameter(b,bname)) )
	{
		boolean z = false;
		value *v = p->values;
		while(v)
		{
			put_value(a,aname,v->name,z,false,false);
			z = true;
			v = v->next;
		}
	}
}

void reqcpy(request *a,const request *b)
{
	if(a && b)
	{
		parameter *p = b->params;

		while(p)
		{
			boolean b = false;
			value *v = p->values;

			while(v)
			{
				put_value(a,p->name,v->name,b,false,false);
				b = true;
				v = v->next;
			}

			if(p->subrequest) set_subrequest(a,p->name,p->subrequest);

			/* For marsgen */
			{
				parameter* q = find_parameter(a, p->name);
				if(q) {
					free_all_values(q->default_values);
					q->default_values = clone_all_values(p->default_values);
				}
			}

			p = p->next;
		}
	}
}

void reqcpy_no_underscores(request *a,const request *b)
{
	if(a && b)
	{
		parameter *p = b->params;

		while(p)
		{
			if(*p->name != '_') {
				boolean b = false;
				value *v = p->values;

				while(v)
				{
					put_value(a,p->name,v->name,b,false,false);
					b = true;
					v = v->next;
				}
			}

			p = p->next;
		}
	}
}

static void _reqmerge(parameter *pa,const parameter *pb,request *a)
{
	const value *vb = pb->values;

	if(pa->name != pb->name)
		return;

	while(vb) {
		value   *va   = pa->values;
		value   *last = 0;
		const char    *nb   = vb->name;
		boolean add   = true;

		while(va)
		{
			if(va->name == nb)
			{
				add = false;
				break;
			}

			last = va;
			va   = va->next;
		}

		if(add)
		{
			value* v = new_value(strcache(nb));
			if(last) last->next = v; else pa->values = v;
			pa->count = 0;
		}

		vb = vb->next;
	}

	if(pb->subrequest)
		set_subrequest(a,pb->name,pb->subrequest);
}

/* Fast version if a && b same */

static boolean _reqmerge1(request *a,const request *b)
{
	parameter *pa = a->params;
	const parameter *pb = b->params;

	while(pa && pb)
	{
		if(pa->name != pb->name)
			return false;

		_reqmerge(pa,pb,a);

		pa = pa->next;
		pb = pb->next;
	}

	return (pa == NULL && pb == NULL);
}

static void _reqmerge2(request *a,const request *b)
{
	const parameter *pb = b->params;

	while(pb)
	{
		parameter* pa = find_parameter( a, pb->name);

		if(pa == NULL)
		{
			value *v = pb->values;
			while(v)
			{
				put_value(a,pb->name,v->name,true,true,false);
				v = v->next;
			}
			if(pb->subrequest) set_subrequest(a,pb->name,pb->subrequest);
		}
		else
		{
			_reqmerge(pa,pb,a);
		}


		pb = pb->next;
	}
}

void reqmerge(request *a,const request *b)
{
	if(a && b)
	{
		if(!_reqmerge1(a,b))
			_reqmerge2(a,b);
	}
}

void unset_param_value(request *r, const char *param, const char *par_val)
/*
    Removes value from the parameter in the request
*/
{
	parameter * p;
	value * v, * q = NULL;

	/* First check if the parameter exists in the request */

	p = find_parameter( r, param);
	if ( p == NULL ) return;

	/* If it does, make sure its values have been counted */

	if ( !p->count ) count_values( r, param);

	/* Run through the values in the parameter */

	v = p->values;
	while(v)
	{
		if ( EQ( v->name, par_val) )          /* if value matchs input */
		{
			if (q) q->next = v->next;        /* delete value from param */
			else p->values = v->next;
			free_one_value(v);

			p->count--;                      /* Update value counter */
			return;
		}
		q = v;
		v = v->next;
	}

	return;
}

/* Quick and dirty hack */
request *string2request(const char *p)
{
	request *r;
	char *tmp = marstmp();
	FILE *f = fopen(tmp,"w");

	if(f == NULL)
	{
		marslog(LOG_EROR|LOG_PERR,"%s",tmp);
		return NULL;
	}

	fprintf(f,"%s\n",p);
	fclose(f);

	r = read_request_file(tmp);
	unlink(tmp);
	return r;
}

void value2string(value *r,char *buf)
{
	if(r) value2string(r->other_names,buf);
	while(r)
	{
		strcat(buf,r->name);
		if(r->next) strcat(buf,"/");
		r = r->next;
	}
}


void parameter2string(parameter *r,char *buf)
{
	char tmp[80];

	while(r)
	{
		if(mars.debug || *r->name != '_')
		{
			sprintf(tmp," ,\n    %-10s = ",r->name);
			strcat(buf,tmp);
			value2string(r->values,buf);
		}
		r = r->next;
	}
	strcat(buf,"\n");
}


char *request2string(const request *r)
{
	static char *buf = NULL;
	off_t n;

	char *tmp = marstmp();
	FILE *f = fopen(tmp,"w");

	if(f == NULL)
	{
		marslog(LOG_EROR|LOG_PERR,"%s",tmp);
		return NULL;
	}
	save_all_requests(f,r);
	fclose(f);

	f = fopen(tmp,"r");
	if(f == NULL)
	{
		marslog(LOG_EROR|LOG_PERR,"%s",tmp);
		return NULL;
	}

	if(buf) free(buf);

	fseek(f,0,2);
	n = ftell(f);
	rewind(f);
	buf = (char*)MALLOC(n+2);

	n = fread(buf,1,n,f);
	buf[n] = 0;

	fclose(f);

	unlink(tmp);

	return buf;
#if 0
	buf[0] = 0;

	if(r)
	{
		strcat(buf,r->name);
		parameter2string(r->params,buf);
		/* strcat('\n'); */
		return buf;
	}
	return "#empty request";
#endif
}


/*
	MARS specific language and test files
*/

request *mars_language(void)
{
	static request *lang = NULL;
	if(lang == NULL)
		lang = read_language_file(mars.langfile);
	return lang;
}

rule *mars_rules(void)
{
	static rule    *test = NULL;
	if(test == NULL)
		test = read_check_file(mars.testfile);
	return test;
}


request *build_mars_request(request *r)
{
	request *lang;
	rule    *test;
	request *s = r;
	long    flags;

	flags = expand_flags(EXPAND_MARS);

	if((lang = mars_language()) == NULL)
		return NULL;

	if((test = mars_rules()) == NULL)
		return NULL;

	r = expand_all_requests(lang,test,r);

	expand_flags(flags);

	free_all_requests(s);

	mars.number_of_requests = count_requests(r);

	return r;
}

static int as_int(const char* p) {
	int n = 0;
	while(*p) {
		if(isdigit(*p)) {
			n = n*10 + (*p - '0');
		}
		p++;
	}
	return n;
}

static request* split_monthly(request* r) {
	parameter *p = NULL;
	value* v;
	request* first = NULL;
	request* last = NULL;

	int yymm = 0;
	int said = 0;
	request *s = NULL;
	request *c = clone_one_request(r);
	int i = 0;

	p = find_parameter(r, "DATE");
	if(p == NULL) {
		marslog(LOG_WARN, "split_monthly: could not find a DATE in request");
		return c;
	}

	unset_value(c, "DATE");

	for(v = p->values; v != NULL; v = v->next)
	{
		const char* date = v->name;
		int ymd = as_int(date);
		if(ymd < 1000000) {
			if(!said) {
				marslog(LOG_WARN, "split_monthly: invalid date: %s (%d)", date, ymd);
			}
		}

		ymd /= 100;
		if(ymd != yymm || s == NULL) {
			yymm = ymd;
			s = clone_one_request(c);
			set_value(s, "DATE", date);

			if(first == NULL) {
				first = last = s;
			}
			else {
				last->next = s;
				last = s;
			}
		}
		else {
			add_value(s, "DATE", date);
		}

	}



	if(first->next) {
		int n = 0;
		s = first;
		while(s) { n++; s = s->next;}
		marslog(LOG_INFO, "Request has been split into %d monthly retrievals", n);
	}

	return first;

}

request *read_mars_request(const char *fname)
{
	request *r, *s;

	if(setjmp(env) != 0) {
		marslog(LOG_EROR,"Request could not be parsed in less than %d seconds (too many dates?)", mars.expand_timeout);
		return NULL;
	}

	signal(SIGALRM,catch_alarm);
	alarm(mars.expand_timeout);

	r = read_request_file(fname);
	s = build_mars_request(r);

	alarm(0);

	if(mars.auto_split_by_dates) {
		marslog(LOG_INFO, "Automatic split by date is on");
		request *t = s;
		request *first = NULL, *last = NULL;
		while(t) {
			request* p = split_monthly(t);
			if(first == NULL) {
				last = first = p;
			}
			else {

				while(last->next) {
					last = last->next;
				}

				last->next = p;
			}

			last = p;
			t = t->next;
		}
		free_all_requests(s);
		s = first;
	}



	return s;
}

err handle_default(request *r,void *data)
{
	reset_language(mars_language());
	return 0;
}

void add_subrequest(request *r,const char *name,const request *s)
{
	parameter *p = find_parameter(r,name);

	if(p && p->subrequest)
		set_subrequest(p->subrequest,"next",s);
	else
		set_subrequest(r,name,s);
}

void set_subrequest(request *r,const char *name,const request *s)
{
	parameter *p;
	if(!r || !name) return;
	if (s == NULL)
	{
		unset_value(r, name);
		return;
	}
	put_value(r,name,"#",false,false,false);
	p = find_parameter(r,name);
	free_all_requests(p->subrequest);

	/* p = find_parameter(r,name); */
	p->subrequest = clone_all_requests(s);
}

request *get_subrequest(const request *r,const char *name,int n)
{
	if(!name) return NULL;
	else
	{
		parameter *p = find_parameter(r,name);
		int i = 0;
		request *s;

		r = p?p->subrequest:NULL;
		while(r && (i < n))
		{
			p = find_parameter(r,"next");
			r = p?p->subrequest:NULL;
			i++;
		}

		s = clone_all_requests(r);
		unset_value(s,"next");
		return s;
	}
}

static struct {
	char *param;
	char *title;
} messages[] =  {
	{"PARAM",      "parameter", },
	{"LEVELIST",   "level", },
	{"DATE",       "date", },
	{"TIME",       "time", },
	{"REFERENCE",  "reference", },
	{"STEP",       "step", },
	{"DIAGNOSTIC", "diagnostic", },
	{"ITERATION",  "iteration", },
	{"NUMBER",     "number", },
	{"DOMAIN",     "domain", },
	{"FREQUENCY",  "frequency", },
	{"DIRECTION",  "direction", },
};

void notify_missing_field(const request* r, const char *name)
{
	if(mars.infomissing)
	{
		int i = 0;
		char tmp[80];
		char buf[10240];
		const char *p = 0;
		char c = ' ';

		strcpy(buf,"Missing");
		for(i = 0; i < NUMBER(messages); i++)
			if((p = get_value(r,messages[i].param,0)) != NULL)
			{
				sprintf(tmp,"%c%s %s",c,messages[i].title,p);
				c=',';
				strcat(buf,tmp);
			}

		marslog(LOG_INFO,"%s",buf);
	}
}

void sort_request(const request *r,int count,char *names[], char *vals[],void *data)
{
	int i;
	request **x = (request**)data;
	for(i=0;i<count;i++)
		 if(vals[i]) add_unique_value(*x,names[i],"%s",vals[i]);
}


int count_requests(const request* r)
{
	int n = 0;

	while(r)
	{
		n++;
		r=r->next;
	}
	return n;

}
