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



/* This is in the FDB, but needs to be recompiled on SGI */
#if defined(sgi)
boolean is_host_local(char *host)
{
	return false;
}
#endif

extern int unsetvalsfdb(int*);

typedef struct data {
	char    *file;
	char    *db_name;
	boolean one;
	boolean unique;
	char    *classe;
	char    *expver;
	char    *stream;
	char    *type;
} data;

#define ADIM(x) (sizeof(x)/sizeof(x[0]))

struct stream_dbname{
	char stream[10];
	char db_name[10];
};

static struct stream_dbname stream2dbtable[] = {
/* List only the ones that are not 'fdb' */
	{"WASF","seas",},
	{"SFMM","seas",},
	{"SWMM","seas",},
	{"SMMA","seas",},
	{"MNFC","seas",},
	{"MNFH","seas",},
	{"MNFA","seas",},
	{"MNFW","seas",},
	{"MFHW","seas",},
	{"MFAW","seas",},
	{"MNFM","seas",},
	{"MFHM","seas",},
	{"MFAM","seas",},
	{"MFWM","seas",},
	{"MHWM","seas",},
	{"MAWM","seas",},
	{"MMSF","seas",},
	{"MSMM","seas",},
	{"WAMS","seas",},
	{"MSWM","seas",},
	{"MMSA","seas",},
	{"EHMM","seas",},
	{"ESMM","seas",},
	{"seas","seas",},
	{"SEAS","ocean",},
	{"OCEA","ocean",},
};
static data setup;

static option opts[] = {    
	/* config, env, opt , default, typ (bool...),offset   */
	{NULL,NULL,"-f",NULL, t_str,sizeof(char*),OFFSET(data,file),},   
	{NULL,NULL,"-d",NULL, t_str,sizeof(char*),OFFSET(data,db_name),},   
	{NULL,"GRIB2FDB_ONE","-1","1", t_boolean,sizeof(boolean),OFFSET(data,one),},   
	{NULL,"GRIB2FDB_EXPVER","-e",NULL,t_str,sizeof(char*),OFFSET(data,expver),},
	{NULL,"GRIB2FDB_CLASS","-c",NULL,t_str,sizeof(char*),OFFSET(data,classe),},
	{NULL,"GRIB2FDB_STREAM","-s",NULL,t_str,sizeof(char*),OFFSET(data,stream),},
	{NULL,"GRIB2FDB_TYPE","-T",NULL,t_str,sizeof(char*),OFFSET(data,type),},
	{NULL,"GRIB2FDB_UNIQUE_FDB",NULL,"1", t_boolean,sizeof(boolean),OFFSET(data,unique),},   
};

int getDBName(request *r, data setup_options, char* format, char* DBname){
	int i;
	int found = 0;

	if (setup_options.db_name){
		strcpy(DBname,setup_options.db_name);
		return 0;
	}

	if (strncmp(format,"BUDG",4) == 0){
		strcpy(DBname,"fdb");
		return 0;
	}

	for (i=0; i<ADIM(stream2dbtable); i++)
		if (strcmp(stream2dbtable[i].stream,get_value(r,"STREAM",0)) == 0) {
			/* marslog(LOG_INFO,"Using database '%s' for stream %s", stream2dbtable[i].db_name, get_value(r,"STREAM",0)); */
			strcpy(DBname,stream2dbtable[i].db_name);
			return 0;
		}

	/* marslog(LOG_INFO,"Using database 'fdb' for stream %s", get_value(r,"STREAM",0)); */
	strcpy(DBname,"fdb");

	return 0;
}

static int DBid(int *addr, request *r, data setup_options,char *format, char* DBname,char*  prevDBname, int makedb_myrank)
{
	int err = 0;
	err = getDBName(r, setup_options, format, DBname);

	if(strcmp(DBname,prevDBname) != 0){
		if (*addr > -1)
			err = closefdb(addr);

		*addr = -1;

		if ((err = openfdb(DBname,addr,"w")) != 0){
			marslog(LOG_EROR,"Makedb : DBid : Cannot open database %s",DBname);
			marsexit(1);     
		}
		printf("repoened with addr %d,  %s\n",*addr ,DBname );
		if(setup_options.unique)
			err = setuniqueidfdb(addr);
		strcpy(prevDBname,DBname);
	}
	return err;
}

static err fdb_check_grib(data setup, const request *g)
{
	if(setup.classe)
	{
		const char *g_val = get_value(g,"CLASS",0);
		const char *u_val = upcase(setup.classe);
		if(!EQ(g_val,u_val))
		{
			marslog(LOG_EROR,"Mismatch between user class '%s' and GRIB class '%s'",u_val,g_val);
			return -1;
		}
	}

	if(setup.expver)
	{
		const char *g_val = get_value(g,"EXPVER",0);
		const char *u_val = upcase(setup.expver);
		if(!EQ(g_val,u_val))
		{
			marslog(LOG_EROR,"Mismatch between user expver '%s' and GRIB expver '%s'",u_val,g_val);
			return -1;
		}
	}

	if(setup.stream)
	{
		const char *g_val = get_value(g,"STREAM",0);
		const char *u_val = upcase(setup.stream);
		if(!EQ(g_val,u_val))
		{
			marslog(LOG_EROR,"Mismatch between user stream '%s' and GRIB stream '%s'",u_val,g_val);
			return -1;
		}
	}

	if(setup.type)
	{
		const char *g_val = get_value(g,"TYPE",0);
		const char *u_val = upcase(setup.type);
		if(!EQ(g_val,u_val))
		{
			marslog(LOG_EROR,"Mismatch between user type '%s' and GRIB type '%s'",u_val,g_val);
			return -1;
		}
	}

	return NOERR;
}

int main(int argc, char **argv)
{
	FILE *fd = 0;
	int e = 0;
	int cnt = 0;
	int dberr = 0;
	long  buflen = 0;
	long length = 0;
	char *buffer = NULL;
	int fdb = -1;
	int makedb_myrank = 0;
	char buf[80];
	char DBname[80];
	char prevDBname[80]; 
	char *FdbHost ;
	char *FdbConfigMode_standalone = "FDB_CONFIG_MODE=standalone";
	char *FdbConfigMode_client     = "FDB_CONFIG_MODE=client";
	char *FdbRoot;

	DBname[0] = 0;
	prevDBname[0] = 0;
	marsinit(&argc,argv,&setup,NUMBER(opts),opts);

	if(!setup.file)
	{
		printf("Usage: %s [ -d database ] -f <file>\n",argv[0]);
		printf("          -f : file to process\n");
		printf("          -d : database to use\n");
		marsexit(1);
	}

	buflen = mars.readany_buffer_size;
	length = buflen;
	buffer = reserve_mem(length);

	start_timer();

	if((fd = fopen(setup.file,"r")) == NULL)
	{
		marslog(LOG_EROR|LOG_PERR,"fopen: %s",setup.file);
		marsexit(1);
	}

	putenv(FdbConfigMode_standalone);

	FdbRoot = getenv("FDB_ROOT");
	FdbHost = getenv("FDB_SERVER_HOST");

	/**FDB setup configuration */
	if (FdbRoot == NULL && FdbHost == NULL)
	{
		marslog(LOG_EROR,"FDB_ROOT or FDB_SERVER_HOST not set");
		marsexit(1);
	}

	if (FdbHost != NULL && !is_host_local(FdbHost))
		putenv(FdbConfigMode_client);
	else
	{
		putenv(FdbConfigMode_standalone);
		if (FdbRoot == NULL) {
			marslog(LOG_EROR,"FDB_ROOT not set");
			marsexit(1);
		}
	}


	initfdb();

	while((e = _readany(fd,buffer,&length)) == NOERR)
	{
		int err = 0;
		int len = (int)length;
		request *g = empty_request("GRIB");
		err = grib_to_request(g,buffer,length);
		if(err != NOERR)
		{
			marslog(LOG_EROR,"Error %d while decoding field number %d. Grib description:",err,cnt);
			print_all_requests(g);	
			marsexit(1);
		}

		dberr = DBid(&fdb, g, setup,"GRIB", DBname,  prevDBname,makedb_myrank);
		if(dberr != NOERR)
		{
			marslog(LOG_EROR,"Error %d while calling DBid()",dberr);
			marsexit(1);
		}

		/* Reset FDB keys for each call. Useful for 2dfd */
		if(setup.one)
		{
			dberr = unsetvalsfdb(&fdb);
			if( dberr != NOERR)
			{
				marslog(LOG_EROR,"Error %d while calling unsetvalsfdb()",dberr);
				marsexit(1);

			}
		}


		if(fdb_check_grib(setup,g) != NOERR)
		{
			marslog(LOG_EROR,"Failed to match GRIB. Exit");
			marsexit(1);
		}
	

		request_to_fdb(fdb,g,1);
		if(mars.debug)
			print_all_requests(g);	
		marslog(LOG_DBUG,"writefdb length: %ld",length);
		/* writefdb returns:
			0 when OK
			<0 on error
		*/
		err = writefdb(&fdb, buffer, &len);
		if(err != NOERR)
		{
			marslog(LOG_EROR,"Error %d while writing to FDB %d bytes",err,len);
			marsexit(1);

		}
		free_all_requests(g);
		length = buflen;
		cnt++;
	}

	if(fdb > -1) closefdb(&fdb);

	switch(e)
	{
		case EOF:
			e = NOERR;

		case NOERR:
			marslog(LOG_INFO,"Decoded %d GRIB messages",cnt);
			break;

		case BUF_TO_SMALL:
			marslog(LOG_WARN,"Buffer is too small (%d bytes, %d needed.)", buflen,length);
			break;
		case NOT_FOUND_7777:
			marslog(LOG_EROR,"Group 7777 not found at the end of GRIB message");
			break;
		default:
			marslog(LOG_EROR,"readany: got unknown error %d",e);
			break;
	}

	if(buffer) release_mem(buffer);

	stop_timer(buf);
	timer_cpu();
	if(*buf) marslog(LOG_INFO,"Request time: %s",buf);

	print_memory_usage(NULL);
	marsexit(e);
}
