/*
 * © 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"
#include <signal.h>

globals mars;

#define THEQUOTE(x) #x
#define QUOTE(x)    THEQUOTE(x)

static option mars_opts[] = {

	NULL,"MARS_HOME",NULL,
	".",
	t_str,sizeof(char*),OFFSET(globals,mars_home),

	"debug","MARS_DEBUG","-debug","0",
	t_boolean,sizeof(boolean),OFFSET(globals,debug),

	"nofork","MARS_NOFORK","-nofork","0",
	t_boolean,sizeof(boolean),OFFSET(globals,nofork),

	"maxforks","MARS_MAXFORKS","-maxforks","20",
	t_int,sizeof(int),OFFSET(globals,maxforks),

	"echo","MARS_ECHO","-echo","0",
	t_boolean,sizeof(boolean),OFFSET(globals,echo),

	"quiet",NULL,"-quiet","0",
	t_boolean,sizeof(boolean),OFFSET(globals,quiet),

	"config","MARS_CONFIG","-config",
	NULL,
	t_str,sizeof(char*),OFFSET(globals,config),

	"emsfile","MARS_EMS_FILE","-emsfile",
	NULL,
	t_str,sizeof(char*),OFFSET(globals,emsfile),

	"authfile","MARS_AUTH_FILE","-authfile",
	"etc/mars.authentication",
	t_str,sizeof(char*),OFFSET(globals,authfile),

	"authmail","MARS_AUTH_MAIL","-authmail",
	"usu",
	t_str,sizeof(char*),OFFSET(globals,authmail),

	"mailer","MARS_MAILER","-mailer",
	"/usr/bin/mailx",
	t_str,sizeof(char*),OFFSET(globals,mailer),

	"monhost","MARS_MONITOR_HOST","-monhost",
	"aramis",
	t_str,sizeof(char*),OFFSET(globals,monhost),

	"monport","MARS_MONITOR_PORT","-monport",
	"8901",
	t_int,sizeof(int),OFFSET(globals,monport),

	"langfile","MARS_LANGUAGE_FILE","-langfile",
	"etc/mars.def",
	t_str,sizeof(char*),OFFSET(globals,langfile),

	"testfile","MARS_TEST_FILE","-testfile",
	"chk/mars.chk",
	t_str,sizeof(char*),OFFSET(globals,testfile),

	"computeflg","MARS_COMPUTE_FLAG",NULL,
	"255",
	t_int,sizeof(int),OFFSET(globals,computeflg),

	"certify",NULL,"-certify",
	"0",
	t_boolean,sizeof(boolean),OFFSET(globals,certify),

	"infomissing","MARS_INFORM_MISSING","-infomissing",
	"0",
	t_boolean,sizeof(boolean),OFFSET(globals,infomissing),

	"dhsmail","MARS_DHS_MAIL","-dhsmail",
	"max@ecmwf.int",
	t_str,sizeof(char*),OFFSET(globals,dhsmail),

	"y2k","MARS_Y2K","-y2k","1",
	t_boolean,sizeof(boolean),OFFSET(globals,y2k),

	"y2k_problem","MARS_Y2K_PROBLEM","-y2k_problem","0",
	t_long,sizeof(long),OFFSET(globals,y2k_problem),

	"y2k_fail","MARS_Y2K_FAIL","-y2k_fail","1",
	t_boolean,sizeof(boolean),OFFSET(globals,y2k_fail),

	"crc","MARS_CRC","-crc","0",
	t_boolean,sizeof(boolean),OFFSET(globals,crc),

	"ignore_7777","MARS_IGNORE_7777","-ignore_7777","0",
	t_boolean,sizeof(boolean),OFFSET(globals,ignore_7777),

	"autoresol","MARS_AUTO_RESOL","-autoresol","1",
	t_boolean,sizeof(boolean),OFFSET(globals,autoresol),

	"autoarch","MARS_AUTO_ARCH","-autoarch","0",
	t_long,sizeof(long),OFFSET(globals,autoarch),

	"sms_label","MARS_SMS_LABEL","-sms_label",NULL,
	t_str,sizeof(char*),OFFSET(globals,sms_label),

	"patch_sst","MARS_PATCH_SST","-patch_sst","0",
	t_boolean,sizeof(boolean),OFFSET(globals,patch_sst),

	"accuracy","MARS_PACK_BITS","-accuracy",
	"0",
	t_int,sizeof(int),OFFSET(globals,accuracy),

	"grib_missing_value","MARS_MISSING_VALUE","-missing_value",
#ifdef __linux__
	"3.40282347E+38F",
#else
	QUOTE(FLOAT_MISSING_VALUE),
#endif
	t_fortfloat,sizeof(fortfloat),OFFSET(globals,grib_missing_value),

	"warning","MARS_WARNING","-warning","1",
	t_boolean,sizeof(boolean),OFFSET(globals,warning),

	"info","MARS_INFO","-info","1",
	t_boolean,sizeof(boolean),OFFSET(globals,info),

	"sockbuf","MARS_SOCKBUF","-sockbuf",
	"524288",
	t_int,sizeof(int),OFFSET(globals,sockbuf),

	"validate","MARS_VALIDATE","-validate",
#ifdef ECMWF
	"ecmwf",
#else
	"null",
#endif
	t_str,sizeof(char*),OFFSET(globals,validate),

	"clients","MARS_CLIENTS","-clients",
	"0",
	t_int,sizeof(int),OFFSET(globals,clients),

	"show_pid","MARS_SHOW_PID","-show_pid",
	"0",
	t_int,sizeof(int),OFFSET(globals,show_pid),

	"statfile","MARS_STATISTICS_FILE","-statfile",NULL,
	t_str,sizeof(char*),OFFSET(globals,statfile),

	"mail_frequency","MARS_MAIL_FREQUENCY","-mail_frequency","0",
	t_long,sizeof(long),OFFSET(globals,mail_frequency),

	"home","MARS_USER_HOME","-home",
	NULL,
	t_str,sizeof(char*),OFFSET(globals,home),

	"max_filesize","MARS_MAX_FILESIZE","-max_filesize",
	"0",
	t_long64,sizeof(long64),OFFSET(globals,max_filesize),

	"maxretries","MARS_MAX_RETRIEVE_RETRY","-maxretries",
	"5",
	t_int,sizeof(int),OFFSET(globals,maxretries),

	"paramtable","MARS_PARAM_TABLE","-paramtable","1",
	t_boolean,sizeof(boolean),OFFSET(globals,paramtable),

	"grib_postproc", "MARS_GRIB_POSTPROC", "-grib_postproc", "1",
	t_boolean,sizeof(boolean),OFFSET(globals,grib_postproc),

	"dont_check_pseudogrib", "MARS_DONT_CHECK_PSEUDOGRIB", "-dont_check_pseudogrib", "0",
	t_boolean,sizeof(boolean),OFFSET(globals,dont_check_pseudogrib),

	"bufr_empty_target", "MARS_BUFR_EMPTY_TARGET", "-bufr_empty_target", "1",
	t_boolean,sizeof(boolean),OFFSET(globals,bufr_empty_target),

	"mm_firstofmonth", "MARS_MM_FIRSTOFMONTH", "-mm_firstofmonth", "0",
	t_boolean,sizeof(boolean),OFFSET(globals,mm_firstofmonth),

	"use_intuvp", "MARS_USE_INTUVP", "-use_intuvp", "1",
	t_boolean,sizeof(boolean),OFFSET(globals,use_intuvp),

	"debug_ems", "MARS_DEBUG_EMS", "-debug_ems", "0",
	t_boolean,sizeof(boolean),OFFSET(globals,debug_ems),

	"can_do_vector_postproc", "MARS_CAN_DO_VECTOR_POSTPROC", "-can_do_vector_postproc", "1",
	t_boolean,sizeof(boolean),OFFSET(globals,can_do_vector_postproc),

	"gridded_observations_postproc", "MARS_GRIDDED_OBSERVATIONS_INTERP", "-gridded_observations_postproc", "0",
	t_boolean,sizeof(boolean),OFFSET(globals,gridded_observations_postproc),

/*
#define SCHEDULE_FAIL   0x01
#define SCHEDULE_INFORM 0x02
#define SCHEDULE_LOG    0x04
#define SCHEDULE_INFORM_FUTURE_CHANGE    0x08
#define SCHEDULE_MAIL   0x10

*** After 1st of February ***
SCHEDULE_FAIL | SCHEDULE_INFORM | SCHEDULE_LOG = 7
SCHEDULE_FAIL | SCHEDULE_INFORM | SCHEDULE_LOG | SCHEDULE_MAIL = 23


*** Before 1st of February ***
SCHEDULE_INFORM_FUTURE_CHANGE | SCHEDULE_LOG = 12
SCHEDULE_INFORM_FUTURE_CHANGE | SCHEDULE_LOG | SCHEDULE_MAIL = 28

*** Other tests ****
SCHEDULE_FAIL | SCHEDULE_INFORM_FUTURE_CHANGE | SCHEDULE_LOG = 13
*/

	"dissemination_schedule", "MARS_DISSEMINATION_SCHEDULE", "-dissemination_schedule", "30",
	t_int,sizeof(int),OFFSET(globals,dissemination_schedule),

	"dissemination_schedule_file","MARS_DISSEMINATION_SCHEDULE_FILE","-dissemination_schedule_file",
	"etc/mars.dissemination.schedule.file",
	t_str,sizeof(char*),OFFSET(globals,dissemination_schedule_file),

	"daily_climatology", NULL, NULL , "0",
	t_boolean,sizeof(boolean),OFFSET(globals,daily_climatology),

	"no_special_fp", "MARS_FDB_NO_SPECIAL_FP", "-no_special_fp", "0",
	t_boolean,sizeof(boolean),OFFSET(globals,no_special_fp),

	"year_for_daily_climatology","MARS_YEAR_DAILY_CLIMATOLOGY","-year_for_daily_climatology",
	"2004",
	t_int,sizeof(int),OFFSET(globals,year_for_daily_climatology),

	"notimers", "MARS_NO_TIMERS", "-notimers", "0",
	t_boolean,sizeof(boolean),OFFSET(globals,notimers),

	"valid_data_includes_fcmonth", "MARS_VALID_DATA_INCLUDES_FCMONTH", NULL, "0",
	t_boolean,sizeof(boolean),OFFSET(globals,valid_data_includes_fcmonth),

	"valid_data_includes_fcmonth", "MARS_VALID_DATA_INCLUDES_FCMONTH", NULL, "0",
	t_boolean,sizeof(boolean),OFFSET(globals,valid_data_includes_fcmonth),

	"exit_on_failed_expand", "MARS_EXIT_ON_FAILED_EXPAND", NULL, "0",
	t_boolean,sizeof(boolean),OFFSET(globals,exit_on_failed_expand),

	"emsaccountsfile","MARS_EMS_ACCOUNTS_FILE","-emsaccountsfile",
	NULL,
	t_str,sizeof(char*),OFFSET(globals,emsaccountsfile),

	"force_setting_ecaccount", "MARS_FORCE_SETTING_ECACCOUNT", NULL, "0",
	t_boolean,sizeof(boolean),OFFSET(globals,force_setting_ecaccount),

	"enable_ecaccount_email", "MARS_ENABLE_ECACCOUNT_EMAIL", NULL, "1",
	t_boolean,sizeof(boolean),OFFSET(globals,enable_ecaccount_email),

	"timers_file","MARS_TIMERS_FILE",NULL,
	NULL,
	t_str,sizeof(char*),OFFSET(globals,timers_file),

	"readdisk_buffer","MARS_READDISK_BUFFER",NULL,
	"0",
	t_int,sizeof(int),OFFSET(globals,readdisk_buffer),

	"ecflow_label","MARS_ECFLOW_LABEL","-ecflow_label",NULL,
	t_str,sizeof(char*),OFFSET(globals,ecflow_label),

	"private_key", "MARS_PRIVATE_KEY", NULL, NULL,
	t_str,sizeof(char*),OFFSET(globals,private_key),

	"build_grib_index", "MARS_BUILD_GRIB_INDEX", NULL, "0",
	t_boolean,sizeof(boolean),OFFSET(globals,build_grib_index),

	"readany_buffer_size","MARS_READANY_BUFFER_SIZE",NULL,
	"67108864",
	t_long64,sizeof(long64),OFFSET(globals,readany_buffer_size),

	"webmars_target","WEBMARS_TARGET","-webmars_target",
	NULL,
	t_str,sizeof(char*),OFFSET(globals,webmars_target),

	"show_hosts", "MARS_SHOW_HOSTS", NULL, "1",
	t_boolean,sizeof(boolean),OFFSET(globals,show_hosts),

	"marslite_mode", "MARS_MARSLITE_MODE", NULL, "0",
	t_boolean,sizeof(boolean),OFFSET(globals,marslite_mode),

	"enforce_ems_category", "MARS_ENFORCE_EMS_CATEGORY", NULL, "1",
	t_boolean,sizeof(boolean),OFFSET(globals,enforce_ems_category),

	NULL,"MARS_EXPAND_TIMEOUT",NULL,
	"0",
	t_int,sizeof(int),OFFSET(globals,expand_timeout),

	NULL, "MARS_AUTO_SPLIT_BY_DATES", NULL, "0",
	t_boolean,sizeof(boolean),OFFSET(globals,auto_split_by_dates),

	NULL, "WEBMARS_KEEP_DATABASE", NULL, "0",
	t_boolean,sizeof(boolean),OFFSET(globals,keep_database),

	"webmars_request_id","WEBMARS_REQUEST_ID",NULL,
	NULL,
	t_str,sizeof(char*),OFFSET(globals,webmars_request_id),

	NULL,"MARS_MAX_RETRIEVE_SIZE",NULL, "0",
	t_long64,sizeof(long64),OFFSET(globals,max_retrieve_size),


};

static int    ac     = 0;
static char **av     = NULL;
static char   me[20];

static char *cmdopt(const char *clss,const char *name,const char *opt,boolean isbool)
{
	int i;

	if(!av) return NULL;

	for(i=0;i<ac;i++)
		if(EQ(opt,av[i])) {
			if(isbool) return "1";
			else
				return av[i+1];
		}

	return NULL;
}

static const char *config(const char *clss,const char *name,const char *opt)
{
	request *r = mars.setup;

	while(r)
	{
		const char *c = get_value(r,"class",0);
		const char *n = get_value(r,"name",0);

		if( clss && c && (EQ(clss,c)))
			if( (name == NULL) || (n != NULL && EQ(name,n)))
				return no_quotes(get_value(r,opt,0));
		r = r->next;
	}
	return NULL;
}

static const char *get_opt_value(const char *clss,const char *name,option *opt)
{
	const char *val = opt->def;  /* default is default, of course */
	const char *p;


	/* then, from config file  */

	if(opt->name && (p = config(clss,name,opt->name)))
		val = p;



	/* then from env */

	if(opt->env && (p = getenv(opt->env)))
		val = p;


	/* then from command options  */

	if(opt->opt && (p =  cmdopt(clss,name,opt->opt,(boolean)(opt->kind == t_boolean))))
		val = p;



	return val;

}

#ifdef NO_ATOLL_SUPPORT
extern long64 atoll(const char *);
#endif

void get_options(const char *clss,const char *name,void *addr,int count,option opts[])
{
	int i;
	int size;

	union {
		char    c;
		int     i;
		long    l;
		long64  ll;
		char   *s;
		void   *p;
		double  d;
		boolean b;
		fortfloat f;
	} u;

	for(i=0;i<count;i++)
	{
		switch(opts[i].kind)
		{
		case t_char:
			u.c = *get_opt_value(clss,name,&opts[i]);
			size = sizeof(char);
			break;

		case t_int:
			u.i = atoi(get_opt_value(clss,name,&opts[i]));
			size = sizeof(int);
			break;

		case t_long:
			u.l = atol(get_opt_value(clss,name,&opts[i]));
			size = sizeof(long);
			break;

		case t_long64:
			u.ll = atoll(get_opt_value(clss,name,&opts[i]));
			size = sizeof(long64);
			break;

		case t_str:
			u.s = strcache(get_opt_value(clss,name,&opts[i]));
			size = sizeof(char*);
			break;

		case t_ptr:
			u.p = NULL;
			marslog(LOG_WARN,"t_ptr options are not implemented.");
			size = sizeof(void*);
			break;

		case t_double:
			u.d = atof(get_opt_value(clss,name,&opts[i]));
			size = sizeof(double);
			break;

		case t_boolean:
			u.b = (boolean)(atoi(get_opt_value(clss,name,&opts[i])) != 0);
			size = sizeof(boolean);
			break;

		case t_fortfloat:
			u.f = (fortfloat)atof(get_opt_value(clss,name,&opts[i]));
			size = sizeof(fortfloat);
			break;

		default:
			marslog(LOG_EXIT,"Error in var_options type=%d",
			    opts[i].kind);
			break;
		}

		if(size != opts[i].size)
			marslog(LOG_EXIT,
			    "Error in var_options size %d != %d (type=%d)",
			    size,opts[i].size,opts[i].kind);

		memcpy((char*)addr+opts[i].offset,(char*)&u,opts[i].size);
	}
}

char *progname(void)
{
	return me;
}

static void trap(int n
#if defined(__cplusplus) || defined(c_plusplus)
    ,...
#endif

)
{
	marslog(LOG_EXIT,"Signal %d received",n);
}

static void toggle_debug(int n
#if defined(__cplusplus) || defined(c_plusplus)
    ,...
#endif
)
{
	marslog(LOG_INFO,"Toggeling debugging");
	mars.debug = (boolean)! mars.debug;
	signal(n,toggle_debug);
}

void trap_all_signals()
{
	signal(SIGINT, trap);
	signal(SIGHUP, trap);
	signal(SIGQUIT,trap);
	signal(SIGTERM,trap);
	signal(SIGALRM,trap);

#if 0
	signal(SIGBUS, trap);
	signal(SIGSEGV,trap);
#endif

#ifndef __i386__
	signal(SIGSYS, trap);
#endif

	signal(SIGUSR2,toggle_debug);

	/* don't trap SIGCHLD, otherwise pclose won't return a usable value */
	/* signal(SIGCHLD,SIG_IGN); */
}

void marsinit(int *argc,char **argv,void *addr,int count,option opts[])
{
	char *p;
	static int done =0;
	if(done) return;
	done =1;

	strncpy(me,mbasename(argv[0]),19);
	me[19] = 0;
	p = me;
	while(*p) {
		if (*p=='.') *p=0;
		p++;
	}



	ac = *argc-1;
	av = argv+1;

	trap_all_signals();

	mars.expflags    = EXPAND_MARS;
	mars.restriction = false;

	get_options("application",me,&mars,NUMBER(mars_opts),mars_opts);

	if(mars.config)
		mars.setup = read_request_file(mars.config);

	get_options("application",me,&mars,NUMBER(mars_opts),mars_opts);

	get_options("application",me,addr, count,opts);

	/* read config */


	/* if(mars.debug) */
	/* print_all_requests(mars.setup); */

	mars.appl = strcache(mbasename(me));
#ifdef sun_bsd
	on_exit(_marsexit,NULL);
#else
	atexit(_marsexit);
#endif

	/* Initialise grib_api settings */
	mars_grib_api_init();

    /* Initialise fdb5 settings */
    mars_fdb5_init(argc, argv);
}

const char* config_file(const char* p)
{
	static char path[1024];
	if(*p != '~') return p;
	sprintf(path,"%s/%s",mars.mars_home,p+1);
	return path;
}
