/*
 * © 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 <setjmp.h>
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include "lang.h"
#include "mars.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",
    "OFFSETDATE",
	"TIME",
    "OFFSETTIME",

    "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,   /* OFFSETDATE */
	eq_default,   /* TIME */
    eq_default,   /* OFFSETTIME */
	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_netcdf(const request* r) {
    const char* t = get_value(r, "LEVTYPE", 0);
    if ((t != NULL) && (EQ(t, "O2D") || EQ(t, "O3D"))) {
        const char* c = get_value(r, "CLASS", 0);
        return (c != NULL) && EQ(c, "EP");
    }

    return 0;
}

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);
}

const char* save_request_file_to_log(const char* fname) {

    if (!fname) {
        fname = copy_stdin_to_tmpfile();
    }

    if (fname) {

        char sdate[24];
        char logfile[256];

        char hostname[80];
        gethostname(hostname, sizeof(hostname));
        time_t t     = time(NULL);
        struct tm tm = *localtime(&t);

        FILE* req = fopen(fname, "r");
        if (!req) {
            marslog(LOG_EROR | LOG_PERR, "Error opening request file %s", fname);
            return NULL;
        }

        fseek(req, 0L, SEEK_END);
        long bufsize = ftell(req);
        char* buffer = malloc(sizeof(char) * (bufsize + 1024));
        if (!buffer) {
            marslog(LOG_EROR, "Failed to allocate %d bytes", bufsize);
            fclose(req);
            return NULL;
        }

        size_t len1 = sprintf(buffer, "### hostname %s pid %d uid %d datetime %d-%02d-%02dT%02d:%02d:%02d\n", hostname, getpid(), getuid(), tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
        fseek(req, 0L, SEEK_SET);
        size_t len2 = fread(buffer + len1, sizeof(char), bufsize, req);
        fclose(req);

        size_t total  = len1 + len2;
        buffer[total] = '\n';
        total++;

        /* Get time stamp */
        strftime(sdate, sizeof(sdate), "%Y%m%d", gmtime(&t));

        /* Choose requests log file */
        sprintf(logfile, "%s.%s.%ld", mars.logreqfile, sdate, marsversion());

        locked_write(logfile, buffer, total);
        free(buffer);
    }

    return fname;
}

#define BUFFERSIZE 1024
const char* copy_stdin_to_tmpfile() {
    /* make tmp file */
    const char* tmpdir = getenv("TMPDIR");
    if (!tmpdir) {
        tmpdir = "/tmp";
    }
    char path[1024];
    sprintf(path, "%s/tmpXXXXXXXXXXX", tmpdir);
    int fd = mkstemp(path);
    if (fd == -1) {
        marslog(LOG_EROR | LOG_PERR, "Failed to create temp request file %s", path);
        return NULL;
    }

    FILE* file = fdopen(fd, "w");
    if (!file) {
        marslog(LOG_EROR | LOG_PERR, "Failed to create FILE* from file descriptor %d for %s", fd, path);
        return NULL;
    }

    // printf("temp mars request file %s\n", path);

    char buffer[BUFFERSIZE];
    for (;;) {
        memset(buffer, '\0', sizeof(char) * BUFFERSIZE);
        size_t bytes = fread(buffer, sizeof(char), BUFFERSIZE, stdin);
        size_t len   = fwrite(buffer, sizeof(char), bytes, file);
        if (bytes != len) {
            marslog(LOG_EROR | LOG_PERR, "Error writing request to temp file %s", path);
            return NULL;
        }
        if (bytes < BUFFERSIZE) {
            if (feof(stdin))
                break;
        }
    }
    if (fclose(file) != 0) {
        marslog(LOG_EROR | LOG_PERR, "Error closing request temp file %s", path);
        return NULL;
    }

    return strcache(path);
}

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_netcdf(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, int n) {
    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 /= n;
        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);

    mars.logrequest = true; /* log the request */
    r               = read_request_file(fname);
    mars.logrequest = false;
    s               = build_mars_request(r);

    alarm(0);

    if (mars.auto_split_by_dates  || mars.auto_split_by_day) {
        marslog(LOG_INFO, "Automatic split on dates is on");
        request* t     = s;
        request *first = NULL, *last = NULL;
        while (t) {
            request* p = split_monthly(t, mars.auto_split_by_day ? 1 : 100);
            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;
}
