/*
 * © 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
