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

#include "mars.h"
#include "mars_client_config.h"

int pproc_initialise(int argc, char** argv);

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),

	"dont_trap_signals", "MARS_DONT_TRAP_SIGNALS", "-dont_trap_signals", "0",
	t_boolean, sizeof(boolean), OFFSET(globals, dont_trap_signals),

	"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),

	"udp_server", "MARS_UDP_SERVER", "-udp_server", NULL, /* hostanme1:port1:hostname2:port2 */
	t_str, sizeof(char*), OFFSET(globals, udp_server),

	"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;
	int e;
	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;


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

	if (!mars.dont_trap_signals) {
		trap_all_signals();
	}

	/* 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);

	e = pproc_initialise(*argc, argv);
	if (e != NOERR) {
		marsexit(e);
	}

#if mars_client_HAVE_UDP_STATS
        mars_eckit_init(argc, argv);
#endif
}

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