/*
 * © 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 "mars.h"
#define HYPERCUBE

#if !defined(NOFDB)

#define PARAM_U 131
#define PARAM_V 132
#define PARAM_VO 138
#define PARAM_D 155

extern int initfdb(void);
extern int openfdb(char*, int*, char*);
extern int openfdb_remote(char*, int*, char*, char*);
extern int readfdb(int*, void*, int*);
extern int writefdb(int*, void*, int*);
extern int closefdb(int*);
extern int setvalfdb(int*, char*, char*);
extern int setvalfdb_f(int*, char*, float);
extern int setvalfdb_i(int*, char*, int);
extern int isfdb_attribute(int*, char*);

static void nfdb_init(void);
static err nfdb_open(void* data, request*, request*, int);
static err nfdb_close(void* data);
static err nfdb_read(void* data, request* r, void* buffer, long* length);
static err nfdb_write(void* data, request* r, void* buffer, long* length);
static boolean nfdb_check(void* data, request* r);

typedef struct fdbdata {
    int ref;
    request* w;
    request* u;
    hypercube* h;
    int index;
    boolean u_and_v;
    boolean retrieve;
    boolean opened;
    char* u_grib;
    char* v_grib;
    long u_len;
    long v_len;
    char* host;
    int retry;
    int timeout;
    char* buffer;
    long length;
    long buflen;
    long pos;
    boolean more;
    boolean y2k;
    boolean expect;
    boolean expect_any;
    char* fdbname;
    timer* fdbtimer;
    int repres;
    boolean nomissing;
    boolean uv_from_fdb;
    boolean leg_done;
    boolean set_table;
} fdbdata;

static option opts[] = {
    {"host", "FDB_SERVER_HOST", NULL, "vpp700-x08", t_str, sizeof(char*), OFFSET(fdbdata, host)},
    {"retry", NULL, NULL, "5", t_int, sizeof(int), OFFSET(fdbdata, retry)},
    {"timeout", NULL, NULL, "120", t_int, sizeof(int), OFFSET(fdbdata, timeout)},
    {"fdbname", NULL, NULL, "fdb", t_str, sizeof(char*), OFFSET(fdbdata, fdbname)},

    {"buffer", "MARS_FDB_BUFFER", NULL, "10485760", t_int, sizeof(int),
     OFFSET(fdbdata, buflen)},
    {"retrieve", "MARS_FDB_RETRIEVE", NULL, "0", t_boolean, sizeof(boolean),
     OFFSET(fdbdata, retrieve)},
    {"y2k", "MARS_FDB_Y2K", NULL, "1", t_boolean, sizeof(boolean),
     OFFSET(fdbdata, y2k)},
    {"nomissing", "MARS_FDB_SUPPRES_MISSING", NULL, "0", t_boolean, sizeof(boolean),
     OFFSET(fdbdata, nomissing)},
    {"uv_from_fdb", "MARS_UV_FROM_FDB", NULL, "1", t_boolean, sizeof(boolean),
     OFFSET(fdbdata, uv_from_fdb)},
    {"set_table", "MARS_FDB_SET_TABLE", NULL, "0", t_boolean, sizeof(boolean),
     OFFSET(fdbdata, set_table)},
};


base_class _nfdbbase = {

    NULL,       /* parent class */
    "nfdbbase", /* name         */

    false, /* inited       */

    sizeof(fdbdata), /* private size */
    NUMBER(opts),    /* option count */
    opts,            /* options      */

    nfdb_init, /* init         */

    nfdb_open,  /* open         */
    nfdb_close, /* close        */

    nfdb_read,  /* read         */
    nfdb_write, /* write        */

    NULL, /* control      */

    nfdb_check, /* check        */

};


base_class* nfdbbase = &_nfdbbase;

static err timed_readfdb(int* ref, void* buffer, int* length, timer* t) {
    int e;
    long64 total = 0;
    timer_start(t);
    if ((e = readfdb(ref, buffer, length)) == NOERR)
        total = *length;
    timer_stop(t, total);

    return e;
}

/*
   n=100000 for step range or fcperiod
   n=1000 for quantile
*/
static long range2fdb(const char* r, long n) {
    const char* p = r + strlen(r);
    stepRange sr;

    sr.from = atol(r);
    sr.to   = 0;

    while (p > r) {
        if (*p == '-' || *p == ':') {
            ++p;
            sr.to = atol(p);
            break;
        }
        --p;
    }
    return (sr.from * n + sr.to);
}


static void nfdb_init(void) {
    initfdb();
}

static int opened = false;
static int first  = true;
static int ref    = 0;

static err setallvalues(fdbdata*, request*);

static void close_fdb(int code, void* data) {
    if (opened) {
        closefdb(&ref);
        opened = false;
    }
}

static err nfdb_open(void* data, request* r, request* ev, int mode) {
    err e               = 0;
    fdbdata* fdb        = (fdbdata*)data;
    const char* p       = get_value(r, "EXPECT", 0);
    const char* fdbmode = getenv("FDB_CONFIG_MODE");

    boolean local = fdbmode && (strcmp(fdbmode, "standalone") == 0);
    char msg[1024];

#ifndef HYPERCUBE
    fdb->u = unwind_one_request(r);
    fdb->w = fdb->u;

#else
    fdb->h = new_hypercube_from_mars_request(r);
    fdb->u = clone_one_request(r);
#endif

    fdb->expect     = p ? atol(p) : 0;
    fdb->expect_any = (p && (atol(p) == 0));

    if (fdb->expect)
        if (count_values(r, "DATABASE") == 1)
            marslog(LOG_INFO, "Trying to retrieve %d fields from the fdb",
                    fdb->expect);

    if (local)
        sprintf(msg, "Transfer from local FDB");
    else
        sprintf(msg, "Transfer from %s", fdb->host);

    fdb->fdbtimer = get_timer(msg, "transfertime", true);

    if (mars.debug) {
        print_all_requests(r);
        print_all_requests(fdb->u);
    }

    /* int setclientfdb(int retries, int timeout); */
    /* setclientfdb(5,60*10); */
    setclientfdb(fdb->retry, fdb->timeout);

    if (!opened) {
        char buf[1024];
        timer* connect_time = 0;

        sprintf(buf, "Connecting to %s", database_name(fdb));
        connect_time = get_timer(buf, NULL, true);

        timer_start(connect_time);
        e = openfdb_remote(fdb->fdbname, &fdb->ref, "r", fdb->host);
        timer_stop(connect_time, 0);
        opened = (e == 0);
        ref    = fdb->ref;
    }
    else
        fdb->ref = ref;

    if (e == 0 && fdb->retrieve) {
        if (fdb->expect) {
            marslog(LOG_WARN, "EXPECT does not work with MARS_FDB_RETRIEVE");
            marslog(LOG_WARN, "mode MARS_FDB_RETRIEVE disabled");
            fdb->retrieve = 0;
        }
        else {
            fdb->more   = true;
            fdb->buffer = reserve_mem(fdb->buflen);
            setallvalues(fdb, r);
            if (first) {
                install_exit_proc(close_fdb, 0);
                first = false;
                marslog(LOG_INFO, "Using MARS_FDB_RETRIEVE flag");
            }
        }
    }

    return e;
}

static err nfdb_close(void* data) {
    fdbdata* fdb = (fdbdata*)data;
    if (fdb->u_grib)
        release_mem(fdb->u_grib);
    if (fdb->v_grib)
        release_mem(fdb->v_grib);
    if (fdb->buffer)
        release_mem(fdb->buffer);
    free_all_requests(fdb->u);

    if (fdb->repres) {
        if (fdb->repres > 1)
            marslog(LOG_WARN, "%d fields were retrieved from a different representation", fdb->repres);
        else
            marslog(LOG_WARN, "1 field was retrieved from a different representation");
    }

#ifdef HYPERCUBE
    free_hypercube(fdb->h);
#endif

    opened = false;
    first  = false;
    return closefdb(&fdb->ref);
}

static void SETVALFDB(int* f, const char* a, const char* b) {
    if (mars.debug)
        marslog(LOG_DBUG, "SETVALFDB: %s %s", a, b);
    setvalfdb(f, (char*)a, (char*)b);
}

static void SETVALFDBF(int* f, const char* a, float b) {
    int s    = b / abs(b);
    float pr = 1000.0;
    float p  = ((int)((b * pr) + (s * 0.499))) / pr;

    if (mars.debug)
        marslog(LOG_DBUG, "SETVALFDB_F: %s %f", a, p);
    setvalfdb_f(f, (char*)a, p);
}

static void SETVALFDB_OCEAN(int* f, const char* a, const char* str) {
    fortfloat b = atof(str);
    int p       = round_decimal(b) * 1000;

    if (mars.debug)
        marslog(LOG_DBUG, "SETVALFDB_OCEAN: %s %d", a, p);
    setvalfdb_i(f, (char*)a, p);
}

static const char* REPRES[] = {
    "sh",
    "gg",
    "ll",
};

static err check_repres(fdbdata* fdb, char* buffer, long* length) {
    const char* repres = get_value(fdb->w, "REPRES", 0);
    const char* res    = repres ? lowcase(repres) : NULL;
    int i;
    err ret = -1;

    if (!res)
        return -1;

    for (i = 0; i < NUMBER(REPRES); i++) {
        int len = *length;


        if (EQ(REPRES[i], res))
            continue;

        marslog(LOG_DBUG, "TRY %s", REPRES[i]);

        setvalfdb(&fdb->ref, "repres", (char*)REPRES[i]);

        if ((ret = timed_readfdb(&fdb->ref, buffer, &len, fdb->fdbtimer)) == 0) {
            *length = len;
            fdb->repres++;
            marslog(LOG_DBUG, "OK %s", REPRES[i]);
            break;
        }
    }
    setvalfdb(&fdb->ref, "repres", (char*)res);
    return ret;
}

static err check_uv(fdbdata* fdb, char* buffer, long* length) {

    err ret       = -1;
    char* vo_grib = NULL;
    char* d_grib  = NULL;
    int vo_len;
    int d_len;

    int p = get_value(fdb->w, "PARAM", 0) ? atoi(get_value(fdb->w, "PARAM", 0)) : 0;

    /* u and v requested */

    if (p != PARAM_U && p != PARAM_V)
        goto fail;

    /* Check if previouly converted */

    if (p == PARAM_U && fdb->u_grib != 0) {
        if (*length < fdb->u_len) {
            ret     = -3;
            *length = fdb->u_len;
            goto fail;
        }
        *length = fdb->u_len;
        memcpy(buffer, fdb->u_grib, fdb->u_len);
        release_mem(fdb->u_grib);
        fdb->u_grib = NULL;
        marslog(LOG_DBUG, "U ready...");
        return 0;
    }

    if (p == PARAM_V && fdb->v_grib != 0) {
        if (*length < fdb->v_len) {
            ret     = -3;
            *length = fdb->v_len;
            goto fail;
        }
        *length = fdb->v_len;
        memcpy(buffer, fdb->v_grib, fdb->v_len);
        release_mem(fdb->v_grib);
        fdb->v_grib = NULL;
        marslog(LOG_DBUG, "V is ready...");
        return 0;
    }

    /* look for vo and d */

    /* Get vorticity */

    setvalfdb(&fdb->ref, "param", "138");
    vo_grib = reserve_mem(vo_len = *length);

    if ((ret = timed_readfdb(&fdb->ref, vo_grib, &vo_len, fdb->fdbtimer)) != 0)
        goto fail;


    /* Get divergence */
    setvalfdb(&fdb->ref, "param", "155");
    d_grib = reserve_mem(d_len = *length);
    if ((ret = timed_readfdb(&fdb->ref, d_grib, &d_len, fdb->fdbtimer)) != 0)
        goto fail;

#if 1
    if (fdb->uv_from_fdb) {
        fdb->uv_from_fdb = false; /* From now, don't try UV anymore */
        marslog(LOG_INFO, "U and V not found in FDB, using VO and D for this request");
    }
#endif

    if (fdb->u_grib)
        release_mem(fdb->u_grib);
    fdb->u_grib = NULL;
    if (fdb->v_grib)
        release_mem(fdb->v_grib);
    fdb->v_grib = NULL;

    if (p == PARAM_U) {
        fdb->v_grib = reserve_mem(fdb->v_len = *length);
        ret         = makeuv(vo_grib, d_grib, vo_len, buffer, fdb->v_grib, length);
        fdb->v_len  = *length;
    }
    else {
        fdb->u_grib = reserve_mem(fdb->u_len = *length);
        ret         = makeuv(vo_grib, d_grib, vo_len, fdb->u_grib, buffer, length);
        fdb->u_len  = *length;
    }


fail:
    if (vo_grib)
        release_mem(vo_grib);
    if (d_grib)
        release_mem(d_grib);

    setvalfdb(&fdb->ref, "param", (char*)get_value(fdb->w, "PARAM", 0));

    return ret;
}

boolean windgust(request* s) {
    boolean windgust = false;
    int i            = 0;
    const char* param;

    while (((param = get_value(s, "PARAM", i++)) != NULL) && !windgust) {
        long par   = 0;
        long table = -1;

        paramtable(lowcase(no_quotes(param)), &par, &table, false);
        if ((par == 49) || (par == 123)) {
            windgust = true;
            if (count_values(s, "PARAM") > 1) {
                marslog(LOG_WARN, "Wind gust STEP needs special handling retrieving from FDB");
                marslog(LOG_WARN, "Please, split your MARS request to retrieve param 49 alone");
            }
        }
    }
    return windgust;
}

static err setallvalues(fdbdata* fdb, request* s) {

    parameter* p               = 0;
    request* r                 = empty_request(0);
    boolean wave_fg            = false;
    const char* type           = get_value(s, "TYPE", 0);
    const char* stream         = get_value(s, "STREAM", 0);
    boolean fp                 = false; /* Forecast Probability */
    boolean stream_enfo        = false;
    boolean stream_enfh        = false;
    const char* ignore_wave_fg = getenv("MARS_IGNORE_WAVE_FG_FDB");

    if (type && stream && EQ(type, "FG") && (EQ(stream, "WAVE") || EQ(stream, "SCWV")))
        wave_fg = true;

    if (type && EQ(type, "FP") && stream && (EQ(stream, "ENFO") || EQ(stream, "WAEF"))) {
        if (!windgust(s))
            fp = true;
    }

    if (stream && EQ(stream, "ENFO"))
        stream_enfo = true;

    if (stream && EQ(stream, "ENFH"))
        stream_enfh = true;

    /* Override behaviour via environment variable */
    /* All this code should go once dissemination can handle step ranges for FP */
    if (mars.no_special_fp)
        fp = false;

    names_loop(s, sort_request, &r);


    p = s->params;
    while (p) {
        if (get_value(r, p->name, 0) == 0)
            valcpy(r, s, p->name, p->name);

        p = p->next;
    }

    /* print_all_requests(r); */

    p = r->params;

    if (get_value(r, "LEVTYPE", 0) && (strcmp(get_value(r, "LEVTYPE", 0), "SFC") == 0))
        setvalsfdb(&fdb->ref, "levelist", "0000");

    while (p) {
        if (mars.debug) {
            char buf[10240];
            value* v = p->values;
            buf[0]   = '\0';
            while (v) {
                strcat(buf, v->name);
                if (v->next)
                    strcat(buf, "/");
                v = v->next;
            }
            marslog(LOG_DBUG, "setvalsfdb: %s %s", p->name, buf);
        }

        if (strcmp(p->name, "STREAM") == 0) {
            stream_enfo = (strcmp(p->values->name, "ENFO") == 0);
            stream_enfh = (strcmp(p->values->name, "ENFH") == 0);
            setvalsfdb(&fdb->ref, p->name, (char*)lowcase(p->values->name));
        }
        else if (strcmp(p->name, "DATE") == 0) {
            int n = 0;
            if (strlen(p->values->name) == 8 && !fdb->y2k)
                n = 2;
            setvalsfdb(&fdb->ref, p->name, (char*)lowcase(no_quotes(p->values->name + n)));
        }
        else if (strcmp(p->name, "LEVTYPE") == 0) {
            char levtype[3];
            int fdb_len = 1; /* Length of levtype in FDB */
            levtype[0]  = p->values->name[0];
            if (strcmp(p->values->name, "DP") == 0) {
                SETVALFDB(&fdb->ref, "levelist", "off");
                SETVALFDB(&fdb->ref, "latitude", "off");
                SETVALFDB(&fdb->ref, "longitude", "off");
            }
            else {
                if (strcmp(p->values->name, "PT") == 0)
                    levtype[0] = 't';
                else if (strcmp(p->values->name, "PV") == 0)
                    levtype[0] = 'v';
                else if (strcmp(p->values->name, "WV") == 0) {
                    /* Ugly: quick hack to get it working */
                    levtype[1] = p->values->name[1];
                    fdb_len    = 2;
                }
            }
            levtype[fdb_len] = '\0';
            setvalsfdb(&fdb->ref, "LEVTYPE", (char*)lowcase(levtype));
        }
        else if (strcmp(p->name, "STEP") == 0 && wave_fg && (ignore_wave_fg == NULL))
            setvalsfdb(&fdb->ref, p->name, "6");
        else if ((strcmp(p->name, "STEP") == 0) || (strcmp(p->name, "FCPERIOD") == 0)) {
            const char* q = 0;
            int i         = 0;
            while (q = get_value(r, p->name, i++)) {
                if (isrange(q)) {
                    char buf[12];
                    /* Once FP in FDB is like EFI (ranges), we can remove
                       next test and only set ranges as p1*100000+p2 */
                    if (!fp) {
                        long s = range2fdb(q, 100000);
                        sprintf(buf, "%ld", s);
                    }
                    else {
                        stepRange sr;
                        str2range(q, &sr);
                        sprintf(buf, "%02l%02l", sr.from / 24, sr.to / 24);
                    }
                    setvalsfdb(&fdb->ref, p->name, buf);
                }
                else
                    setvalsfdb(&fdb->ref, p->name, q);
            }
        }
        else if (strcmp(p->name, "QUANTILE") == 0) {
            const char* q = 0;
            int i         = 0;
            while (q = get_value(r, p->name, i++)) {
                if (isrange(q)) {
                    char buf[12];
                    long s = range2fdb(q, 1000);

                    sprintf(buf, "%ld", s);
                    setvalsfdb(&fdb->ref, p->name, buf);
                }
                else
                    setvalsfdb(&fdb->ref, p->name, q);
            }
        }
        else if (strcmp(p->name, "PARAM") == 0) {
            const char* q = 0;
            int i         = 0;
            while (q = get_value(r, p->name, i++)) {
                long par   = 0;
                long table = -1;

                paramtable(lowcase(no_quotes(q)), &par, &table, false);
                if (table >= 210 || table == 201 || table == 162 || (fdb->set_table && table != -1) || (stream_enfo && (table == 171 || table == 173)) || (stream_enfh && (table == 151))) {
                    char npar[7];
                    sprintf(npar, "%ld%03ld", table, par);
                    setvalsfdb(&fdb->ref, p->name, npar);
                }
                else
                    setvalsfdb(&fdb->ref, p->name, (char*)lowcase(no_quotes(q)));
            }
        }
        else if (strcmp(p->name, "LEVELIST") == 0) {
            const char* q = 0;
            int i         = 0;
            while (q = get_value(r, p->name, i++)) {
                char buf[5];

                if (atof(q) == atol(q))
                    sprintf(buf, "%04ld", atol(q));
                else {
                    int n = 4 - strlen(q);
                    strcpy(buf, q);
                    while (n-- > 0)
                        strcat(buf, "0");
                }
                setvalsfdb(&fdb->ref, "levelist", buf);
            }
        }
        else {
            const char* q = 0;
            int i         = 0;
            while (q = get_value(r, p->name, i++))
                setvalsfdb(&fdb->ref, p->name, (char*)lowcase(no_quotes(q)));
        }
        p = p->next;
    }

    free_all_requests(r);
    return 0;
}

static fortint fdbseek(void* data, fortint offset, fortint whence) {
    marslog(LOG_WARN, "No seek backwards on fdb");
    return -1;
}

static fortint fdbtell(void* data) {
    fdbdata* fdb = (fdbdata*)data;
    return fdb->pos;
}

static long read1(void* data, void* buf, long len) {
    fdbdata* fdb = (fdbdata*)data;

    if (fdb->pos >= fdb->length) {
        if (fdb->more) {
            err ret     = 0;
            fdb->pos    = 0;
            fdb->length = fdb->buflen;
            ret         = retrievefdb(&fdb->ref, fdb->buffer, &fdb->length);
            if (ret != 0 && ret != -103)
                return -1;
            fdb->more = (ret == -103);
        }
        else
            return -1;
    }

    if (fdb->length - fdb->pos < len)
        len = fdb->length - fdb->pos;

    memcpy(buf, fdb->buffer + fdb->pos, len);

    fdb->pos += len;

    return len;
}

void request_to_fdb(int ref, request* r, int y2k) {
    err ret         = 0;
    parameter* p    = r->params;
    boolean wave_fg = false;
    int llen;
    boolean ocean       = false;
    int parm            = 0;
    boolean fp          = false; /* Forecast Probability */
    const char* type    = 0;
    boolean has_param   = true;
    const char* levtype = get_value(r, "LEVTYPE", 0);
    const char* stream  = get_value(r, "STREAM", 0);
    const char* leg     = get_value(r, "_LEG_NUMBER", 0);
    boolean stream_enfo = false;
    boolean stream_enfh = false;

    const char* ignore_wave_fg = getenv("MARS_IGNORE_WAVE_FG_FDB");


    /* It's part of structure "fdb", but it can't be passed as parameter */
    const char* mars_fdb_set_table = getenv("MARS_FDB_SET_TABLE");
    boolean set_table              = false;
    if (mars_fdb_set_table != NULL)
        set_table = EQ(mars_fdb_set_table, "1");

    if (levtype && (strcmp(levtype, "SFC") == 0))
        SETVALFDB(&ref, "levelist", "0000");

    type = get_value(r, "TYPE", 0);
    if (type && EQ(type, "FP") && stream && (EQ(stream, "ENFO") || EQ(stream, "WAEF"))) {
        if (!windgust(r))
            fp = true;
    }

    if (stream && EQ(stream, "ENFO"))
        stream_enfo = true;

    if (stream && EQ(stream, "ENFH"))
        stream_enfh = true;

    /* Override behaviour via environment variable */
    /* All this code should go once dissemination can handle step ranges for FP */
    if (mars.no_special_fp)
        fp = false;

    /* Special case for grib2fdb of hindcast 2d-wave spectra*/
    if (stream && (EQ(stream, "ENWH") || EQ(stream, "EWHO"))) {
        SETVALFDB(&ref, "frequency", "off");
        SETVALFDB(&ref, "direction", "off");
    }


    if (levtype && strcmp(levtype, "DP") == 0) {
        ocean = true;
        SETVALFDB(&ref, "levelist", "off");
        SETVALFDB(&ref, "latitude", "off");
        SETVALFDB(&ref, "longitude", "off");
    }

    if (leg != NULL) {
        if (atoi(leg) != 0)
            SETVALFDB(&ref, "leg", no_quotes(leg));
    }
    else {
#ifdef p690
#if 0
		if(isfdb_attribute(&ref,"leg"))
			SETVALFDB(&ref,"leg","1");
#endif
#endif
    }

    while (p) {
        if ((p->name[0] == '_') || (strcmp(p->name, "REPRES") == 0)) {
            /* Skip */
        }
        else if (strcmp(p->name, "STREAM") == 0) {
            if (strcmp(p->values->name, "DA") == 0)
                SETVALFDB(&ref, "stream", "oper");
            else if (strcmp(p->values->name, "SD") == 0)
                SETVALFDB(&ref, "stream", "supd");
            else if (strcmp(p->values->name, "WAVE") == 0) {
                if (strcmp(get_value(r, "TYPE", 0), "FG") == 0)
                    wave_fg = true;
                SETVALFDB(&ref, "stream", "wave");
            }
            else if (strcmp(p->values->name, "SCWV") == 0) {
                if (strcmp(get_value(r, "TYPE", 0), "FG") == 0)
                    wave_fg = true;
                SETVALFDB(&ref, "stream", "scwv");
            }
            else if (strcmp(p->values->name, "EF") == 0)
                SETVALFDB(&ref, "stream", "enfo");
            else if (strcmp(p->values->name, "SF") == 0)
                SETVALFDB(&ref, "stream", "sens");
            else if (strcmp(p->values->name, "MO") == 0) {
                SETVALFDB(&ref, "stream", "mnth");
            }
            else
                SETVALFDB(&ref, p->name, lowcase(p->values->name));
        }
        else if (strcmp(p->name, "DATE") == 0) {
            int n = 0;
            if (strlen(p->values->name) == 8 && !y2k)
                n = 2;
            SETVALFDB(&ref, p->name, lowcase(no_quotes(p->values->name + n)));
        }
        else if (strcmp(p->name, "LEVTYPE") == 0) {
            char levtype[3];
            int fdb_len = 1; /* Length of levtype in FDB */
            levtype[0]  = p->values->name[0];
            if (strcmp(p->values->name, "DP") != 0) {
                if (strcmp(p->values->name, "PT") == 0)
                    levtype[0] = 't';
                else if (strcmp(p->values->name, "PV") == 0)
                    levtype[0] = 'v';
                else if (strcmp(p->values->name, "WV") == 0) {
                    /* Ugly: quick hack to get it working */
                    levtype[1] = p->values->name[1];
                    fdb_len    = 2;
                }
            }
            levtype[fdb_len] = '\0';
            SETVALFDB(&ref, "LEVTYPE", (char*)lowcase(levtype));
        }
        else if (strcmp(p->name, "LEVELIST") == 0 && !ocean) {
            char buf[5];

            if (atof(p->values->name) == atol(p->values->name))
                sprintf(buf, "%04ld", atol(p->values->name));
            else {
                int n = 4 - strlen(p->values->name);
                strcpy(buf, p->values->name);
                while (n-- > 0)
                    strcat(buf, "0");
            }
            SETVALFDB(&ref, "levelist", buf);
        }
        else if (strcmp(p->name, "STEP") == 0 && wave_fg && (ignore_wave_fg == NULL))
            SETVALFDB(&ref, p->name, "6");
        else if ((strcmp(p->name, "STEP") == 0) || (strcmp(p->name, "FCPERIOD") == 0)) {
            if (isrange(p->values->name)) {
                char buf[12];
                if (!fp) {
                    long s = range2fdb(p->values->name, 100000);
                    sprintf(buf, "%ld", s);
                }
                else {
                    stepRange sr;
                    str2range(p->values->name, &sr);
                    sprintf(buf, "%02d%02d", sr.from / 24, sr.to / 24);
                }
                SETVALFDB(&ref, p->name, buf);
            }
            else {
                SETVALFDB(&ref, p->name, lowcase(no_quotes(p->values->name)));
            }
        }
        else if (strcmp(p->name, "QUANTILE") == 0) {
            if (isrange(p->values->name)) {
                char buf[12];
                long s = range2fdb(p->values->name, 1000);

                sprintf(buf, "%ld", s);
                SETVALFDB(&ref, p->name, buf);
            }
            else {
                SETVALFDB(&ref, p->name, lowcase(no_quotes(p->values->name)));
            }
        }
        else if (strcmp(p->name, "PARAM") == 0) {
            const char* q = lowcase(no_quotes(p->values->name));
            int i         = 0;
            long par      = 0;
            long table    = -1;
            char npar[7];

            paramtable(q, &par, &table, false);
            if (table >= 210 || table == 201 || table == 162 || (set_table && table != -1) || (stream_enfo && (table == 171 || table == 173)) || (stream_enfh && (table == 151)))
                sprintf(npar, "%ld%03ld", table, par);
            else
                sprintf(npar, "%ld", par);
            SETVALFDB(&ref, p->name, npar);
        }
        else if ((strcmp(p->name, "LEVELIST") == 0 && ocean) || (strcmp(p->name, "LATITUDE") == 0) || (strcmp(p->name, "LONGITUDE") == 0)) {
            SETVALFDB_OCEAN(&ref, p->name, p->values->name);
        }
        else
            SETVALFDB(&ref, p->name, lowcase(no_quotes(p->values->name)));
        p = p->next;
    }
}

static err _nfdb_read(void* data, request* r, void* buffer, long* length) {
    fdbdata* fdb = (fdbdata*)data;
    err ret      = 0;
    parameter* p;
    long len        = *length;
    boolean wave_fg = false;
    int llen;
    boolean ocean     = false;
    int parm          = 0;
    boolean fp        = false; /* Forecast Probability */
    const char* type  = 0;
    boolean has_param = true;


#ifdef HYPERCUBE
    request* z = get_cubelet(fdb->h, fdb->index++);
    fdb->w     = z;
#endif

    if (fdb->retrieve) {
        long64 total = 0;
        timer_start(fdb->fdbtimer);
        ret = wmo_read_any_from_stream(fdb, read1, buffer, length);
        if (ret == 0)
            total = *length;
        timer_stop(fdb->fdbtimer, total);

        if (ret)
            return ret;


        if (ret == 0 && r && !observation(r) && !track(r)) {
            grib_to_request(r, buffer, *length);
            set_value(r, "_ORIGINAL_FIELD", "1");
        }

        if ((ret != BUF_TO_SMALL) && (ret != 0))
            notify_missing_field(fdb->w, fdb->host);

        return ret;
    }

    if (fdb->w == NULL)
        return EOF;

#if 0
	if(!fdb->leg_done)
	{
		if(isfdb_attribute(&ref,"leg"))
			SETVALFDB(&ref,"leg","off");
		fdb->leg_done = true;
	}
#endif

    request_to_fdb(fdb->ref, fdb->w, fdb->y2k);

    has_param = (get_value(fdb->w, "PARAM", 0) != NULL);
    /* Fail if parameter is either U or V */
    /* Don't even read the FDB */
    parm = has_param ? atoi(get_value(fdb->w, "PARAM", 0)) : 0;
    if (!fdb->uv_from_fdb && (parm == PARAM_U || parm == PARAM_V)) {
        ret = -1;
    }
    else {
        marslog(LOG_DBUG, "before readfdb");
        llen    = *length;
        ret     = timed_readfdb(&fdb->ref, buffer, &llen, fdb->fdbtimer);
        *length = llen;
        marslog(LOG_DBUG, "after readfdb, ret %d, len %d", ret, len);
    }

    if ((ret == -1 || ret == -2) && has_param) {
        *length = len;
        ret     = check_uv(fdb, buffer, length);
        if (ret == -1 || ret == -2) {
            *length = len;
            ret     = check_repres(fdb, buffer, length);
        }
    }

    if ((ret == -1 || ret == -2) && (fdb->expect != 0 || fdb->expect_any))
        return -42;

    if (ret == 0 && r && !observation(r) && !track(r)) {
        *length = grib_length(buffer, *length);
        grib_to_request(r, buffer, *length);
        set_value(r, "_ORIGINAL_FIELD", "1");
    }

    if (ret != BUF_TO_SMALL && ret != 0)
        notify_missing_field(fdb->w, fdb->host);

#ifndef HYPERCUBE
    fdb->w = fdb->w->next;
#endif

    return ret;
}

static err nfdb_read(void* data, request* r, void* buffer, long* length) {
    fdbdata* fdb = (fdbdata*)data;
    err ret      = _nfdb_read(data, r, buffer, length);

    while ((ret == -42) && (fdb->expect != 0 || fdb->expect_any)) {
        /* If expect is set or expect any, try to get more */

        /* Inform about missing fields */
        if (!fdb->nomissing)
            notify_missing_field(fdb->w, fdb->host);
        ret = _nfdb_read(data, r, buffer, length);
    }

    return ret;
}

static err nfdb_write(void* data, request* r, void* buffer, long* length) {
    marslog(LOG_EXIT, "FDB write no implemeted");
    return EOF;
}

static boolean nfdb_check(void* data, request* r) {
    if (track(r))
        return true;

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

    return true;
}

#else
extern base_class _nullbase;
base_class* nfdbbase = &_nullbase;
#endif
