#include "mda.h"
#include <expat.h>

#ifndef WIN32
#define stricmp(a,b) strcasecmp(a,b)
#endif

#ifdef WIN32
ssize_t getline (char **lineptr, size_t *n, FILE *stream)
{
	size_t count = 0;
	int c;
	char *newLineptr;

	if (!*lineptr) {
		*n = (*n > 0) ? *n : 128;
		*lineptr = (char*)calloc(*n, sizeof(char));
	}

	if (!*lineptr) {
		return -1;
	}

	while ((c = fgetc(stream)) != EOF) {
		count++;

		if (*n <= count) {
			*n = *n * 2 > count ? *n * 2 : count * 2;
			newLineptr = (char*)realloc(*lineptr, *n);
			if (!newLineptr) {
				return -1;
			}

			*lineptr = newLineptr;
		}

		*(*lineptr + count - 1) = c;
		*(*lineptr + count) = '\0';

		if (c == '\n') {
			return count;
		}
	}

	return -1;
}
#endif

int parse_setting (char *p, char **setting, char **value)
{
	*setting = p;
	*value = NULL;
	size_t i = 0;
	while (p[i++] != '\0') {
		if (isspace ((unsigned char)p[i])) {
			p[i++] = '\0';

			while (isspace ((unsigned char)p[i]))
				i++;

			if (p[i] == '\0' )
				return 0;

			*value = &p[i];
			break;
		}
	}

	/* if the value starts with a $ then let's read it from the environment */
	if (*value != NULL && **value == '$') {
		*value = getenv (*value + 1);

		/* if the env is not found then use the $value as the value */
		if (*value == NULL)
			*value = &p[i];
	}

	return 1;
}

int read_config ()
{
	memset (&config.mod_config, 0, sizeof config.mod_config);
	config.maxdeletes = 10000;
#ifndef WIN32
	config.change_user_early = 1;
#endif
	FILE *f= fopen ( config.configfilename, "r" );
	
	if (!f)
		return -1;

	char *line = NULL;
	size_t len = 0;
	ssize_t ret;
	enum cfg_state state = MDA_CONFIG_STATE_START;

	while ((ret = getline (&line, &len, f)) != -1) {
		while (ret > 0 && (line[ret - 1] == '\n' || line[ret - 1] == '\r')) {
			ret--;
			line[ret] = '\0';
		}

		char *p = line;

		while (isspace((unsigned char)*p))
			p++;	

		if (*p == '#' || *p == '\0')
			continue;

		if (!stricmp (p, "Millistream")) {
			state = MDA_CONFIG_STATE_MILLISERVER;
		} else if (!stricmp (p, "Database")) {
			state = MDA_CONFIG_STATE_DATABASE;
		} else if (!stricmp (p, "Application")) {
			state = MDA_CONFIG_STATE_MDA;
		} else if (!stricmp (p, "Log")) {
			state = MDA_CONFIG_STATE_LOGGING;
		} else {
			char *_setting, *_value;

			if (0 == parse_setting (p, &_setting, &_value))
				continue;

			switch (state) {
				case MDA_CONFIG_STATE_START:
					break;

				case MDA_CONFIG_STATE_MILLISERVER: {
					if (0 == strcmp (_setting, "username")) {
						free (config.millistreamusername);
						config.millistreamusername = strdup (_value);
					} else if (0 == strcmp (_setting, "password")) {
						free (config.millistreampassword);
						config.millistreampassword = strdup (_value);
					} else if (0 == strcmp (_setting, "server") && receivers_num == 0) {
						receivers = realloc (receivers, (sizeof *receivers) * (receivers_num + 1));
						receivers[receivers_num].server = strdup (_value);
						receivers[receivers_num].username = NULL;
						receivers[receivers_num].password = NULL;
						receivers[receivers_num].live = 0;
						receivers[receivers_num].handle = mdf_create ();
						receivers[receivers_num].connected = false;
						receivers[receivers_num].received_all_basicdata = false;
						receivers[receivers_num].r_ignore = true;
						receivers_num++;
					} else if (0 == strcmp (_setting, "+server")) {
						receivers = realloc (receivers, (sizeof *receivers) * (receivers_num + 1));
						receivers[receivers_num].server = strdup (_value);
						receivers[receivers_num].username = (config.millistreamusername == NULL) ? NULL : strdup (config.millistreamusername);
						receivers[receivers_num].password = (config.millistreampassword == NULL) ? NULL : strdup (config.millistreampassword);
						receivers[receivers_num].live = 0;
						receivers[receivers_num].handle = mdf_create ();
						receivers[receivers_num].connected = false;
						receivers[receivers_num].received_all_basicdata = false;
						receivers[receivers_num].r_ignore = false;
						receivers[receivers_num].r_trades = config.requestTrades != -1;
						receivers[receivers_num].r_order = config.requestOrderbooks != -1;
						receivers[receivers_num].r_basic = config.requestBasicdata != -1;
						receivers[receivers_num].r_quote = config.requestQuotes != -1;
						receivers[receivers_num].r_quotebbo = config.requestQuotesBBO != -1;
						receivers[receivers_num].r_quoteex = config.requestQuotesEx != -1;
						receivers[receivers_num].r_ph = config.requestPricehistory != -1;
						receivers[receivers_num].r_ca = config.requestCorporateactions != -1;
						receivers[receivers_num].r_news = config.requestNews != -1;
						receivers[receivers_num].r_fund = config.requestFundamentals != -1;
						receivers[receivers_num].r_tradestate = config.requestTradestate != -1;
						receivers[receivers_num].r_perf = config.requestPerformance != -1;
						receivers[receivers_num].r_keyr = config.requestKeyratios != -1;
						receivers[receivers_num].r_estimates = config.requestEstimates != -1;
						receivers[receivers_num].r_noii = config.requestNOII != -1;
						receivers[receivers_num].r_l10n = config.requestL10N != -1;
						receivers[receivers_num].r_ci = config.requestCI != -1;
						receivers[receivers_num].r_ept = config.requestPRIIP != -1;
						receivers[receivers_num].r_emt = config.requestMIFID != -1;
						receivers[receivers_num].r_mappings = config.requestMappings != -1;
						receivers[receivers_num].r_greeks = config.requestGreeks != -1;
						receivers[receivers_num].r_mbo = config.requestMBO != -1;
						receivers[receivers_num].r_eet = config.requestEET != -1;
						receivers_num++;
					} else if (0 == strcmp (_setting, "reconnectinterval"))
						config.reconnectinterval = strtol (_value, NULL, 10);
					else if (0 == strcmp (_setting, "requesttrades") && strcmp (_value, "0") == 0)
						config.requestTrades = -1;
					else if (0 == strcmp (_setting, "requestorderbooks") && strcmp (_value, "0") == 0)
						config.requestOrderbooks = -1;
					else if (0 == strcmp (_setting, "requestquotes") && strcmp (_value, "0") == 0)
						config.requestQuotes = -1;
					else if (0 == strcmp (_setting, "requestquotesbbo") && strcmp (_value, "0") == 0)
						config.requestQuotesBBO = -1;
					else if (0 == strcmp (_setting, "requestquotesex") && strcmp (_value, "0") == 0)
						config.requestQuotesEx = -1;
					else if (0 == strcmp (_setting, "requestpricehistory") && strcmp (_value, "0") == 0)
						config.requestPricehistory = -1;
					else if (0 == strcmp (_setting, "requestnews") && strcmp (_value, "0") == 0)
						config.requestNews = -1;
					else if (0 == strcmp (_setting, "requestbasicdata") && strcmp (_value, "0") == 0)
						config.requestBasicdata = -1;
					else if (0 == strcmp (_setting, "requestcorporateactions") && strcmp (_value, "0") == 0)
						config.requestCorporateactions = -1;
					else if (0 == strcmp (_setting, "requestfundamentals") && strcmp (_value, "0") == 0)
						config.requestFundamentals = -1;
					else if (0 == strcmp (_setting, "requesttradestate") && strcmp (_value, "0") == 0)
						config.requestTradestate = -1;
					else if (0 == strcmp (_setting, "requestperformance") && strcmp (_value, "0") == 0)
						config.requestPerformance = -1;
					else if (0 == strcmp (_setting, "requestkeyratios") && strcmp (_value, "0") == 0)
						config.requestKeyratios = -1;
					else if (0 == strcmp (_setting, "requestestimates") && strcmp (_value, "0") == 0)
						config.requestEstimates = -1;
					else if (0 == strcmp (_setting, "requestnoii") && strcmp (_value, "0") == 0)
						config.requestNOII = -1;
					else if (0 == strcmp (_setting, "requestl10n") && strcmp (_value, "0") == 0)
						config.requestL10N = -1;
					else if (0 == strcmp (_setting, "requestci") && strcmp (_value, "0") == 0)
						config.requestCI = -1;
					else if (0 == strcmp (_setting, "requestpriip") && strcmp (_value, "0") == 0)
						config.requestPRIIP = -1;
					else if (0 == strcmp (_setting, "requestmifid") && strcmp (_value, "0") == 0)
						config.requestMIFID = -1;
					else if (0 == strcmp (_setting, "requestmappings") && strcmp (_value, "0") == 0)
						config.requestMappings = -1;
					else if (0 == strcmp (_setting, "requestgreeks") && strcmp (_value, "0") == 0)
						config.requestGreeks = -1;
					else if (0 == strcmp (_setting, "requestmbo") && strcmp (_value, "0") == 0)
						config.requestMBO = -1;
					else if (0 == strcmp (_setting, "requesteet") && strcmp (_value, "0") == 0)
						config.requestEET = -1;
					else if (0 == strcmp (_setting, "instrumenthashsize"))
						config.databasehashsize = strtoul (_value, NULL, 10);
					else if (0 == strcmp (_setting, "queuehashsize"))
						config.queuehashsize = strtoul (_value, NULL, 10);
					else if (0 == strcmp (_setting, "insrefs"))
						config.insrefs = strdup (_value);

					break;
				}

				case MDA_CONFIG_STATE_DATABASE: {
					if (!strcmp ( _setting, "instance"))
						config.modulefile = strdup (_value);
					else if (!strcmp ( _setting, "updateinterval"))
						config.mintimebetweenupdates = strtol (_value, NULL, 10);
					else if (!strcmp ( _setting, "server"))
						config.mod_config.host = strdup (_value);
					else if (!strcmp ( _setting, "port"))
						config.mod_config.port = strdup (_value);
					else if (!strcmp ( _setting, "username"))
						config.mod_config.username = strdup (_value);
					else if (!strcmp ( _setting, "password"))
						config.mod_config.password = strdup (_value);
					else if (!strcmp ( _setting, "connectionstring"))
						config.mod_config.connectionstring = strdup (_value);
#ifdef WIN32
					else if (!strcmp ( _setting, "domainuser"))
						config.domainuser = strdup (_value);
					else if (!strcmp ( _setting, "domainpassword"))
						config.domainpassword = strdup (_value);
					else if (!strcmp ( _setting, "domain"))
						config.domain = strdup (_value);
#endif
					else if (!strcmp ( _setting, "dbname"))
						config.mod_config.database = strdup (_value);
					else if (!strcmp ( _setting, "keepdeleted"))
						config.databasekeepdeleted = strtol (_value, NULL, 10);
					else if (!strcmp ( _setting, "checkdatabase"))
						config.checkdatabase = (uint8_t) strtol (_value, NULL, 10);
					else if (!strcmp ( _setting, "localtime"))
						config.uselocaltime = (uint8_t) strtol (_value,NULL, 10);
					else if (!strcmp ( _setting, "deletelimit"))
						config.instrumentslimit = strtoull (_value, NULL, 10);
					else if (!strcmp ( _setting, "maxdeletes"))
						config.maxdeletes = strtoul (_value, NULL, 10);
					else if (!strcmp ( _setting, "transactions"))
						config.transactions = strtoull (_value, NULL, 10);
					else if (!strcmp ( _setting, "aggtrades"))
						config.aggtrades = strtol (_value, NULL, 10);
					else if (!strcmp ( _setting, "quoteexspecial"))
						config.quoteExspecial = strtol (_value, NULL, 10);
					else if (!strcmp ( _setting, "ssl_key"))
						config.mod_config.ssl_key = strdup (_value);
					else if (!strcmp ( _setting, "ssl_cert"))
						config.mod_config.ssl_cert = strdup (_value);
					else if (!strcmp ( _setting, "ssl_ca"))
						config.mod_config.ssl_ca = strdup (_value);
					else if (!strcmp ( _setting, "ssl_capath"))
						config.mod_config.ssl_capath = strdup (_value);
					else if (!strcmp ( _setting, "ssl_cipher"))
						config.mod_config.ssl_cipher = strdup (_value);
					else if (!strcmp ( _setting, "ignoremillisec") || !strcmp ( _setting, "truncatetime")) {
						int val = strtol (_value, NULL, 10);

						if (val == 1)
							config.max_timelen = 8;  /* HH:MM:SS */
						else if (val == 2)
							config.max_timelen = 12; /* HH:MM:SS.mmm */
						else
							config.max_timelen = 15; /* HH:MM:SS.mmmuuu */
					} else if (!strcmp ( _setting, "disableupsert")) {
						if (strcmp (_value, "1") == 0 || strcmp (_value, "Y") == 0 || strcmp (_value, "y") == 0)
							config.disable_upsert = true;
						else
							config.disable_upsert = false;
					}

					break;
				}

				case MDA_CONFIG_STATE_LOGGING: {
					if (!strcmp (_setting, "logsqlpath")) {
						if (_value == NULL) break;
						config.logsqlpath = malloc(strlen(_value)+20);
						sprintf(config.logsqlpath,"%s",_value);
#ifndef WIN32
						if (config.logsqlpath[strlen(config.logsqlpath)-1] != '/' )
							strcat(config.logsqlpath,"/");
#else
						if (config.logsqlpath[strlen(config.logsqlpath)-1] != '\\' )
							strcat(config.logsqlpath,"\\");
#endif
					} else if (!strcmp (_setting, "facility")) {
						config.logfacility = strdup (_value);
					} else if (!strcmp (_setting, "host")) {
						config.loghost = strdup (_value);
					} else if (!strcmp (_setting, "debug")) {
						config.debuglog = strtol (_value, NULL, 10);
					}

					break;
				}

				case MDA_CONFIG_STATE_MDA: {
					if (!strcmp (_setting, "tabledefinitions"))
						config.tabledeffile = strdup (_value);
#ifndef WIN32
					else if (!strcmp (_setting, "pidfile"))
						config.pidfile = strdup ( _value);
					else if (!strcmp (_setting, "user"))
						config.applicationuser = strdup (_value);
					else if (!strcmp (_setting, "group"))
						config.applicationgroup = strdup (_value);
					else if (!strcmp (_setting, "switch_user_early"))
						config.change_user_early = strtol (_value, NULL, 10);
#endif
					break;
				}

			}
		}
	}

	free (line);
	fclose(f);

	if (config.insrefs == NULL)
		config.insrefs = strdup ("*");

	return 1;
}
#ifndef WIN32
int	load_module()
{
	config.dbhandle = dlopen(config.modulefile, RTLD_LAZY);
	if (!config.dbhandle) {
		write_to_log (LOG_CRIT,"Failed to open modulefile: %s, error:%s",config.modulefile, dlerror());
		return -1;
	}
	dlerror();    /* Clear any existing error */

	modfunc.init = (int(*)(void))dlsym( config.dbhandle, "mod_init");
	if (dlerror() != NULL)  {
		write_to_log (LOG_CRIT,"Failed to find function init in modulefile: %s, error:%s",config.modulefile, dlerror());
		return -1;
	}

	modfunc.connect = (int(*)(void *))dlsym( config.dbhandle, "mod_connect");
	if (dlerror() != NULL)  {
		write_to_log (LOG_CRIT,"Failed to find function connect in modulefile: %s, error:%s",config.modulefile, dlerror());
		return -1;
	}

	modfunc.disconnect = (int(*)(void *))dlsym( config.dbhandle, "mod_disconnect");
	if (dlerror() != NULL)  {
		write_to_log (LOG_CRIT,"Failed to find function disconnect in modulefile: %s, error:%s",config.modulefile, dlerror());
		return -1;
	}

	modfunc.getlasterror = (int(*)(void *))dlsym( config.dbhandle, "mod_getlasterror");
	if (dlerror() != NULL)  {
		write_to_log (LOG_CRIT,"Failed to find function getlasterror in modulefile: %s, error:%s",config.modulefile, dlerror());
		return -1;
	}

	modfunc.geterrorstring = (char *(*)(void *))dlsym( config.dbhandle, "mod_geterrorstring");
	if (dlerror() != NULL)  {
		write_to_log (LOG_CRIT,"Failed to find function geterrorstring in modulefile: %s, error:%s",config.modulefile, dlerror());
		return -1;
	}

	modfunc.executesql = (int(*)(void *, char *))dlsym( config.dbhandle, "mod_executesql");
	if (dlerror() != NULL)  {
		write_to_log (LOG_CRIT,"Failed to find function executesql in modulefile: %s, error:%s",config.modulefile, dlerror());
		return -1;
	}

	modfunc.exit = (int(*)(void))dlsym( config.dbhandle, "mod_exit");
	if (dlerror() != NULL)  {
		write_to_log (LOG_CRIT,"Failed to find function exit in modulefile: %s, error:%s",config.modulefile, dlerror());
		return -1;
	}

	modfunc.add_value = (char *(*)(void *, char*, size_t))dlsym( config.dbhandle, "mod_add_value");
	if (dlerror() != NULL)  {
		write_to_log (LOG_CRIT,"Failed to find function add_value in modulefile: %s, error:%s",config.modulefile, dlerror());
		return -1;
	}

	modfunc.create = (void *(*)(mod_config *))dlsym( config.dbhandle, "mod_create");
	if (dlerror() != NULL)  {
		write_to_log (LOG_CRIT,"Failed to find function create in modulefile: %s, error:%s",config.modulefile, dlerror());
		return -1;
	}

	modfunc.destroy = (int(*)(void *))dlsym( config.dbhandle, "mod_destroy");
	if (dlerror() != NULL)  {
		write_to_log (LOG_CRIT,"Failed to find function destroy in modulefile: %s, error:%s",config.modulefile, dlerror());
		return -1;
	}

	modfunc.get_status = (uint8_t(*)(void *))dlsym( config.dbhandle, "mod_get_status");
	if (dlerror() != NULL)  {
		write_to_log (LOG_CRIT,"Failed to find function get_status in modulefile: %s, error:%s",config.modulefile, dlerror());
		return -1;
	}

	modfunc.set_status = (void(*)(void *, uint8_t))dlsym( config.dbhandle, "mod_set_status");
	if (dlerror() != NULL)  {
		write_to_log (LOG_CRIT,"Failed to find function set_status in modulefile: %s, error:%s",config.modulefile, dlerror());
		return -1;
	}

	modfunc.get_databaseinfo = (Databaseinfo *(*)(void *))dlsym( config.dbhandle, "mod_get_databaseinfo");
	if (dlerror() != NULL)  {
		write_to_log (LOG_CRIT,"Failed to find function get_databaseinfo in modulefile: %s, error:%s",config.modulefile, dlerror());
		return -1;
	}

	modfunc.free_databaseinfo = (void(*)(Databaseinfo *))dlsym( config.dbhandle, "mod_free_databaseinfo");
	if (dlerror() != NULL)  {
		write_to_log (LOG_CRIT,"Failed to find function free_databaseinfo in modulefile: %s, error:%s",config.modulefile, dlerror());
		return -1;
	}

	modfunc.querysql = (void *(*)(void *, char *,int*))dlsym( config.dbhandle, "mod_querysql");
	if (dlerror() != NULL) {
		write_to_log (LOG_CRIT,"Failed to find function querysql in modulefile: %s, error:%s",config.modulefile, dlerror());
		return -1;
	}

	modfunc.freeresult = (void(*)(void *))dlsym( config.dbhandle, "mod_freeresult");
	if (dlerror() != NULL) {
		write_to_log (LOG_CRIT,"Failed to find function freeresult in modulefile: %s, error:%s",config.modulefile, dlerror());
		return -1;
	}

	modfunc.get_info = (int (*)(void *, mod_info *, int))dlsym( config.dbhandle, "mod_get_info");
	if (dlerror() != NULL ) {
		write_to_log (LOG_CRIT,"Failed to find function get_info in modulefile: %s, error:%s",config.modulefile, dlerror());
		return -1;
	}

	modfunc.add_column = (uint8_t(*)(void *, char *, char *, char *, char *, uint8_t, int, int))dlsym( config.dbhandle, "mod_add_column");
	if (dlerror() != NULL ) {
		write_to_log (LOG_CRIT,"Failed to find add_column_command in modulefile: %s, error:%s",config.modulefile, dlerror());
		return -1;
	}
	modfunc.reconnect = (void(*)(void *))dlsym( config.dbhandle, "mod_reconnect");
	if (dlerror() != NULL ) {
		write_to_log (LOG_CRIT,"Failed to find reconnect in modulefile: %s, error:%s",config.modulefile, dlerror());
		return -1;
	}

	if (modfunc.init() != 1) {
		write_to_log (LOG_CRIT,"Error initializing module, mod_init() failed");
		return -1;
	}
	return 1;
}
#else
int	load_module()
{
	/* we do this dance routine to be able to distinguish between module cannot be found and module cannot be loaded due to
	 * missing dependencies et al, since GetLastError() returns the same code in both cases... */
	config.dbhandle =  LoadLibraryEx (config.modulefile, NULL, DONT_RESOLVE_DLL_REFERENCES);
	if(config.dbhandle == NULL ) {
		write_to_log (LOG_CRIT,"Failed to open modulefile: %s, error:%d",config.modulefile, GetLastError());
		return -1;
	}
	FreeLibrary (config.dbhandle);

	config.dbhandle =  LoadLibrary(config.modulefile);

	if(config.dbhandle == NULL ) {
		write_to_log (LOG_CRIT,"Failed to load modulefile: %s, error:%d",config.modulefile, GetLastError());
		return -1;
	}

	modfunc.create = (void *(*)(mod_config *))GetProcAddress( config.dbhandle, "mod_create");
	if (modfunc.create == NULL ) {
		write_to_log (LOG_CRIT,"Failed to find function create in modulefile: %s, error:%d",config.modulefile, GetLastError());
		return -1;
	}

	modfunc.init = (int(*)(void))GetProcAddress( config.dbhandle, "mod_init");
	if (modfunc.init == NULL ) {
		write_to_log (LOG_CRIT,"Failed to find function init in modulefile: %s, error:%d",config.modulefile, GetLastError());
		return -1;
	}

	modfunc.connect = (int(*)(void *))GetProcAddress( config.dbhandle,"mod_connect");
	if (modfunc.connect == NULL ) {
		write_to_log (LOG_CRIT,"Failed to find function connect in modulefile: %s, error:%d",config.modulefile, GetLastError());
		return -1;
	}

	modfunc.destroy = (int(*)(void *))GetProcAddress( config.dbhandle, "mod_destroy");
	if (modfunc.destroy == NULL ) {
		write_to_log (LOG_CRIT,"Failed to find function destroy in modulefile: %s, error:%d",config.modulefile, GetLastError());
		return -1;
	}

	modfunc.disconnect = (int(*)(void *))GetProcAddress( config.dbhandle, "mod_disconnect");
	if (modfunc.disconnect == NULL ) {
		write_to_log (LOG_CRIT,"Failed to find function disconnect in modulefile: %s, error:%d",config.modulefile, GetLastError());
		return -1;
	}

	modfunc.getlasterror = (int(*)(void *))GetProcAddress( config.dbhandle, "mod_getlasterror");
	if (modfunc.getlasterror == NULL ) {
		write_to_log (LOG_CRIT,"Failed to find function getlasterror in modulefile: %s, error:%d",config.modulefile, GetLastError());
		return -1;
	}

	modfunc.geterrorstring = (char *(*)(void *))GetProcAddress( config.dbhandle, "mod_geterrorstring");
	if (modfunc.geterrorstring == NULL ) {
		write_to_log (LOG_CRIT,"Failed to find function geterrorstring in modulefile: %s, error:%d",config.modulefile, GetLastError());
		return -1;
	}

	modfunc.executesql = (int(*)(void *, char *))GetProcAddress( config.dbhandle, "mod_executesql");
	if (modfunc.executesql == NULL ) {
		write_to_log (LOG_CRIT,"Failed to find function executesql in modulefile: %s, error:%d",config.modulefile, GetLastError());
		return -1;
	}

	modfunc.exit = (int(*)(void))GetProcAddress( config.dbhandle, "mod_exit");
	if (modfunc.exit == NULL ) {
		write_to_log (LOG_CRIT,"Failed to find function exit in modulefile: %s, error:%d",config.modulefile, GetLastError());
		return -1;
	}

	modfunc.add_value = (char *(*)(void *, char*, size_t))GetProcAddress( config.dbhandle, "mod_add_value");
	if (modfunc.add_value == NULL ) {
		write_to_log (LOG_CRIT,"Failed to find function add_value in modulefile: %s, error:%d",config.modulefile, GetLastError());
		return -1;
	}

	modfunc.set_status = (void(*)(void *, uint8_t))GetProcAddress( config.dbhandle, "mod_set_status");
	if (modfunc.set_status == NULL ) {
		write_to_log (LOG_CRIT,"Failed to find function set_status in modulefile: %s, error:%d",config.modulefile, GetLastError());
		return -1;
	}

	modfunc.get_status = (uint8_t(*)(void *))GetProcAddress( config.dbhandle, "mod_get_status");
	if (modfunc.get_status == NULL ) {
		write_to_log (LOG_CRIT,"Failed to find function get_status in modulefile: %s, error:%d",config.modulefile, GetLastError());
		return -1;
	}

	modfunc.get_databaseinfo = (Databaseinfo *(*)(void *))GetProcAddress( config.dbhandle, "mod_get_databaseinfo");
	if (modfunc.get_databaseinfo == NULL ) {
		write_to_log (LOG_CRIT,"Failed to find function get_databaseinfo in modulefile: %s, error:%d",config.modulefile, GetLastError());
		return -1;
	}

	modfunc.free_databaseinfo = (void(*)(Databaseinfo *))GetProcAddress( config.dbhandle, "mod_free_databaseinfo");
	if (modfunc.free_databaseinfo == NULL ) {
		write_to_log (LOG_CRIT,"Failed to find function free_databaseinfo in modulefile: %s, error:%d",config.modulefile, GetLastError());
		return -1;
	}

	modfunc.querysql = (void *(*)(void *, char *, int *))GetProcAddress( config.dbhandle, "mod_querysql");
	if (modfunc.querysql == NULL ) {
		write_to_log (LOG_CRIT,"Failed to find function querysql in modulefile: %s, error:%d",config.modulefile, GetLastError());
		return -1;
	}

	modfunc.freeresult = (void(*)(void *))GetProcAddress( config.dbhandle, "mod_freeresult");
	if (modfunc.freeresult == NULL ) {
		write_to_log (LOG_CRIT,"Failed to find function freeresult in modulefile: %s, error:%d",config.modulefile, GetLastError());
		return -1;
	}

	modfunc.get_info = (int (*)(void *, mod_info *, int))GetProcAddress( config.dbhandle, "mod_get_info");
	if (modfunc.get_info == NULL ) {
		write_to_log (LOG_CRIT,"Failed to find function get_info in modulefile: %s, error:%d",config.modulefile, GetLastError());
		return -1;
	}

	modfunc.add_column = (uint8_t(*)(void *, char *, char *, char *, char *, uint8_t, int, int))GetProcAddress( config.dbhandle, "mod_add_column");
	if (modfunc.add_column == NULL ) {
		write_to_log (LOG_CRIT,"Failed to find add_column in modulefile: %s, error:%d",config.modulefile, GetLastError());
		return -1;
	}

	modfunc.reconnect = (void(*)(void *))GetProcAddress( config.dbhandle, "mod_reconnect");
	if (modfunc.reconnect == NULL ) {
		write_to_log (LOG_CRIT,"Failed to find reconnect in modulefile: %s, error:%d",config.modulefile, GetLastError());
		return -1;
	}

	if (modfunc.init() != 1) {
		write_to_log (LOG_CRIT,"Error initializing module, mod_init() failed");
		return -1;
	}
	
	return 1; 
}
#endif

static void XMLCALL start(void *data, const char *el, const char **attr)
{
Tabledef	*_tabledef = (Tabledef*)data;
int i;

	if (strcmp(el,"definitions") == 0) {
		for (i = 0; attr[i]; i += 2) {
			if (0== strcmp(attr[i],"version")) {
				config.file_templates_ver = strtoull(attr[i+1],NULL,10);
			}
		}
	} else if (strcmp(el,"message") == 0) {
		for (i = 0; attr[i]; i += 2) {
			if (0== strcmp(attr[i],"mref")) {
				_tabledef->messagenumber = strtoul(attr[i+1],NULL,10);
				_tabledef->messages = strdup(attr[i+1]);
			} else if (0== strcmp(attr[i],"table")) {
				_tabledef->tablename = strdup(attr[i+1]);
			} else if (0== strcmp(attr[i],"insertfirst")) {
				_tabledef->insertfirst = attr[i+1][0] == 'y' ? 1:0;
			}
		}
	} else if (strcmp(el,"field") == 0) {
		for (i = 0; attr[i]; i += 2) {
			if (0== strcmp(attr[i],"tag")) {
				_tabledef->fieldnumber = strdup(attr[i+1]);
			} else if (0== strcmp(attr[i],"column")) {
				_tabledef->fieldname = strdup ( attr[i+1] );
				if (MDA_TYPE_TRADE == _tabledef->messagenumber ) {
					if (strcmp (_tabledef->fieldnumber, "4294967295") == 0) /* MDF_F_UNIXTIME */
						config.useunixtime = 1;
				} else if (MDA_TYPE_QUOTE == _tabledef->messagenumber ) {
					if (strcmp (_tabledef->fieldnumber,"insref") && strcmp (_tabledef->fieldnumber,"3") && strcmp (_tabledef->fieldnumber,"4") ) { // insref, time , date should not be nulled
						if (strlen (_tabledef->fieldname) + config.quotefieldsused > config.quotefieldslen) {
							config.quotefieldslen += strlen (_tabledef->fieldname) + 256 ;
							config.quotefields = realloc(config.quotefields, config.quotefieldslen);
						}
						config.quotefieldsused += sprintf(config.quotefields + config.quotefieldsused,"%s = NULL, ",_tabledef->fieldname);
						config.quotefieldscount++;
					}
				} else if (MDA_TYPE_QUOTEBBO == _tabledef->messagenumber ) {
					if (strcmp (_tabledef->fieldnumber,"insref") && strcmp (_tabledef->fieldnumber,"3") && strcmp (_tabledef->fieldnumber,"4") ) { // insref, time , date should not be nulled
						if (strlen (_tabledef->fieldname) + config.quotebbofieldsused > config.quotebbofieldslen) {
							config.quotebbofieldslen += strlen (_tabledef->fieldname) + 256 ;
							config.quotebbofields = realloc(config.quotebbofields, config.quotebbofieldslen);
						}
						config.quotebbofieldsused += sprintf(config.quotebbofields + config.quotebbofieldsused,"%s = NULL, ",_tabledef->fieldname);
						config.quotebbofieldscount++;
					}
				} else if (MDA_TYPE_NETORDERIMBALANCE == _tabledef->messagenumber ) {
					if (strcmp (_tabledef->fieldnumber,"insref") && strcmp (_tabledef->fieldnumber,"3") && strcmp (_tabledef->fieldnumber,"4") ) { // insref, time , date should not be nulled
						if (strlen (_tabledef->fieldname) + config.noiifieldsused > config.noiifieldslen) {
							config.noiifieldslen += strlen (_tabledef->fieldname) + 256 ;
							config.noiifields = realloc(config.noiifields, config.noiifieldslen);
						}
						config.noiifieldsused += sprintf(config.noiifields + config.noiifieldsused,"%s = NULL, ",_tabledef->fieldname);
						config.noiifieldscount++;
					}
				} else if (MDA_TYPE_GREEKS == _tabledef->messagenumber ) {
					if (strcmp (_tabledef->fieldnumber,"insref") && strcmp (_tabledef->fieldnumber,"3") && strcmp (_tabledef->fieldnumber,"4") ) { // insref, time , date should not be nulled
						if (strlen (_tabledef->fieldname) + config.greeksfieldsused > config.greeksfieldslen) {
							config.greeksfieldslen += strlen (_tabledef->fieldname) + 256 ;
							config.greeksfields = realloc(config.greeksfields, config.greeksfieldslen);
						}
						config.greeksfieldsused += sprintf(config.greeksfields + config.greeksfieldsused,"%s = NULL, ",_tabledef->fieldname);
						config.greeksfieldscount++;
					}
				}
			} else if (0== strcmp(attr[i],"column2")) {
				_tabledef->fieldname2 = strdup ( attr[i+1] );
			} else if (0== strcmp(attr[i],"factor")) {
				_tabledef->factor = strdup ( attr[i+1] );
			} else if (0== strcmp(attr[i],"key")) {
				_tabledef->keyvalue = (uint8_t) strtoul (attr[i+1],NULL,10);
			} else if(0 == strcmp(attr[i],"type")) {
				_tabledef->type = strdup(attr[i+1]);

				if (strcmp (_tabledef->type, "double") == 0)
					_tabledef->numerical = 1;
				else if (strcmp (_tabledef->type, "int") == 0)
					_tabledef->numerical = 1;
				else if (strcmp (_tabledef->type, "smallint") == 0)
					_tabledef->numerical = 1;
				else if (strcmp (_tabledef->type, "bigint") == 0)
					_tabledef->numerical = 1;
				else if (strcmp (_tabledef->type, "decimal") == 0)
					_tabledef->numerical = 1;
			} else if(0 == strcmp(attr[i],"len")) {
				_tabledef->len = strdup(attr[i+1]);
			} else if(0 == strcmp(attr[i],"nullable")) {
				_tabledef->nullable = strtol(attr[i+1],NULL,10);
			} else if(0 == strcmp(attr[i],"unsigned")) {
				_tabledef->_unsigned = strtol(attr[i+1],NULL,10);
			} else if(0 == strcmp(attr[i],"calc")) {
				_tabledef->calc = strtol(attr[i+1],NULL,10);
			}
		}
	}
}
static void XMLCALL end(void *data, const char *el)
{
Tabledef	*_tabledef = (Tabledef*)data;
	
	if( 0 == strcmp(el,"message")) {
		if (MDA_TYPE_QUOTE == _tabledef->messagenumber ) {
			config.quotefields[strlen(config.quotefields)-2] = '\0';
		} else if (MDA_TYPE_QUOTEBBO == _tabledef->messagenumber ) {
			config.quotebbofields[strlen(config.quotebbofields)-2] = '\0';
		} else if (MDA_TYPE_NETORDERIMBALANCE == _tabledef->messagenumber ) {
			config.noiifields[strlen(config.noiifields)-2] = '\0';
		} else if (MDA_TYPE_GREEKS == _tabledef->messagenumber ) {
			config.greeksfields[strlen(config.greeksfields)-2] = '\0';
		}
		free(_tabledef->tablename);
		free(_tabledef->messages);
		_tabledef->messages = NULL;
		_tabledef->tablename = NULL;
		_tabledef->messagenumber = -1;
		/*	end of messagetype */
	}
	else
	if( 0 == strcmp(el,"definitions")) {
		return;	/* end of file */
	}
	else if (0 == strcmp (el, "field")) {
		char *p = _tabledef->messages;
		char	_hashkey[256];
		for(;;) {
			Tabledef *_newdef;
			uint32_t messageno;
			while(isdigit((unsigned char)*p) == 0 && *p != '\0') p++;

			if(*p == '\0') break;
			messageno = strtoul(p,&p,10);

			/* only send requests for those messages that we have in our tabledef since it's
			 * a major waste to request and process messages that we will not use!
			 * -1 is set by load_config() if the user has decided to disable the message and thus
			 * a signal for us not to enable it even though we got it in the tabledef.
			*/
			switch (messageno) {
				case MDA_TYPE_TRADE :
					if (config.requestTrades != -1)
						config.requestTrades = 1;

					break;

				case MDA_TYPE_QUOTE :
					if (config.requestQuotes != -1)
						config.requestQuotes = 1;

					break;

				case MDA_TYPE_QUOTEBBO :
					if (config.requestQuotesBBO != -1)
						config.requestQuotesBBO = 1;

					break;

				case MDA_TYPE_QUOTEEX :
					if (config.requestQuotesEx != -1)
						config.requestQuotesEx = 1;

					break;

				case MDA_TYPE_ADJUSTEDPRICEHISTORY :
				case MDA_TYPE_PRICEHISTORY :
					if (config.requestPricehistory != -1)
						config.requestPricehistory = 1;

					break;

				case MDA_TYPE_BASICDATA :
					if (config.requestTradestate != -1)
						config.requestTradestate = 1;

					if (config.requestBasicdata != -1) {
						config.requestBasicdata = 1;

						if (receivers_num != 0 && receivers[0].r_ignore)
							receivers[0].r_basic = true;
					}

					break;

				case MDA_TYPE_CORPORATEACTION :
					if (config.requestCorporateactions != -1)
						config.requestCorporateactions = 1;

					break;

				case MDA_TYPE_NEWS :
					if (config.requestNews != -1)
						config.requestNews = 1;

					break;

				case MDA_TYPE_ORDERBOOK :
				case MDA_TYPE_BIDORDER :
				case MDA_TYPE_ASKORDER :
					if (config.requestOrderbooks != -1)
						config.requestOrderbooks = 1;

					break;

				case MDA_TYPE_FUNDAMENTALS :
					if (config.requestFundamentals != -1)
						config.requestFundamentals = 1;

					break;

				case MDA_TYPE_DIFFCALC :
					if (config.requestQuotes != -1)
						config.requestQuotes = 1;

					if (config.requestQuotesEx != -1)
						config.requestQuotesEx = 1;

					if (config.requestPerformance != -1)
						config.requestPerformance = 1;

					break;

				case MDA_TYPE_PERFORMANCE :
					if (config.requestPerformance != -1)
						config.requestPerformance = 1;

					break;

				case MDA_TYPE_KEYRATIOS :
					if (config.requestKeyratios != -1)
						config.requestKeyratios = 1;

					break;

				case MDA_TYPE_ESTIMATES : 
				case MDA_TYPE_ESTIMATESHISTORY :
					if (config.requestEstimates != -1)
						config.requestEstimates = 1;

					break;

				case MDA_TYPE_NETORDERIMBALANCE :
					if (config.requestNOII != -1)
						config.requestNOII = 1;

					break;

				case MDA_TYPE_L10N :
					if (config.requestL10N != -1)
						config.requestL10N = 1;

					break;

				case MDA_TYPE_CI : 
				case MDA_TYPE_CIHISTORY :
					if (config.requestCI != -1)
						config.requestCI = 1;

					break;

				case MDA_TYPE_PRIIP :
					if (config.requestPRIIP != -1)
						config.requestPRIIP = 1;

					break;

				case MDA_TYPE_MIFID :
				case MDA_TYPE_MIFIDHISTORY :
					if (config.requestMIFID != -1)
						config.requestMIFID = 1;

					break;

				case MDA_TYPE_SECTORS :
					if (config.requestMappings != -1)
						config.requestMappings = 1;

					break;

				case MDA_TYPE_GREEKS :
					if (config.requestGreeks != -1)
						config.requestGreeks = 1;

					break;

				case MDA_TYPE_MBO :
					if (config.requestMBO != -1)
						config.requestMBO = 1;

					break;

				case MDA_TYPE_EET :
					if (config.requestEET != -1)
						config.requestEET = 1;

					break;
			}

			sprintf (_hashkey,"%" PRIu32 ":%s", messageno,_tabledef->fieldnumber);
			_newdef = calloc (1,sizeof (Tabledef));
			_newdef->tablename = strdup(_tabledef->tablename);
			_newdef->hashkey = strdup(_hashkey);
			_newdef->fieldname = strdup (_tabledef->fieldname);
			_newdef->fieldname2 = _tabledef->fieldname2 == NULL ? NULL : strdup (_tabledef->fieldname2);
			_newdef->factor = _tabledef->factor == NULL ? NULL : strdup (_tabledef->factor);
			_newdef->fieldnumber = strdup(_tabledef->fieldnumber);
			_newdef->messagenumber = messageno;
			_newdef->numerical = _tabledef->numerical;
			_newdef->keyvalue = _tabledef->keyvalue;
			_newdef->insertfirst = _tabledef->insertfirst;
			_newdef->type = _tabledef->type == NULL ? NULL : strdup(_tabledef->type);
			_newdef->len = _tabledef->len == NULL ? NULL : strdup(_tabledef->len);

			if (_newdef->len != NULL && _newdef->type != NULL && (strcmp (_newdef->type, "varchar") == 0 || strcmp (_newdef->type, "char") == 0))
				_newdef->maxlen = strtoull (_newdef->len, NULL, 10);

			_newdef->nullable = _tabledef->nullable;
			_newdef->_unsigned = _tabledef->_unsigned;
			_newdef->calc = _tabledef->calc;

			hashit_insert (Tabledefinitions,_newdef->hashkey,_newdef);

			if (_newdef->calc != 0 && _newdef->fieldname2 == NULL) {
				_newdef->fieldname2 = malloc (strlen (_newdef->fieldname) + 6);
				sprintf (_newdef->fieldname2, "%s_base", _newdef->fieldname);
			}

			if (messageno == MDA_TYPE_KEYRATIOS && _newdef->calc != 0) {
				config.keyratios = realloc (config.keyratios, sizeof (Tabledef *) * (config.keyratios_num + 1));
				config.keyratios[config.keyratios_num++] = _newdef;
			}
		}
		_tabledef->numerical = 0;
		_tabledef->keyvalue = 0;
		free(_tabledef->fieldname);
		_tabledef->fieldname = NULL;
		free(_tabledef->fieldname2);
		_tabledef->fieldname2 = NULL;
		free(_tabledef->factor);
		_tabledef->factor = NULL;
		free(_tabledef->fieldnumber);
		_tabledef->fieldnumber = NULL;
		free(_tabledef->len);
		_tabledef->len = NULL;
		free(_tabledef->type);
		_tabledef->type = NULL;
		_tabledef->nullable = 0;
		_tabledef->_unsigned =0;
		_tabledef->calc =0;
	}
}

int	read_table_definitions ()
{
FILE	*f;
XML_Parser _parser;
char	_buffer[1024];
Tabledef tabledef;
int	_len = 0;

	f = fopen (config.tabledeffile,"r");
	if(NULL == f)
		return -1;

	_parser = XML_ParserCreate(NULL);
	if (!_parser) {
		write_to_log(LOG_CRIT,"XML_ParserCreate failed exiting");
		fclose(f);
		return -1;
	}
	config.quotefields = malloc (1024);
	config.quotefieldslen = 1024;
	config.quotebbofields = malloc (1024);
	config.quotebbofieldslen = 1024;
	config.noiifields = malloc (1024);
	config.noiifieldslen = 1024;
	config.greeksfields = malloc (1024);
	config.greeksfieldslen = 1024;
	
	memset (&tabledef, 0, sizeof tabledef);
	XML_SetUserData(_parser,&tabledef);
	XML_SetElementHandler(_parser, start, end);
	while (0 != (_len = fread(_buffer,1,sizeof(_buffer),f))) {
		if (XML_Parse(_parser, _buffer, _len, 0) == XML_STATUS_ERROR) {
			XML_ParserFree(_parser);
			fclose (f);
			write_to_log(LOG_CRIT,"Failed to parse tabledefinitions");
			return -1;
		}
	}
	XML_ParserFree(_parser);
	fclose (f);
	return 1;
}

static struct linebuffer *read_linebuffer (gzFile f, struct linebuffer *line)
{
	const int linesize = 1024;
	int pos = 0, tagged = 0;
	int c;

	if (f == NULL || line == NULL)
		return NULL;

	while ((c = gzgetc (f)) != -1) {
		if (pos + 3 >= line->blen) {
			char *tmp = realloc (line->buffer, line->blen + linesize);

			if (tmp == NULL)
				return NULL;

			line->buffer = tmp;
			line->blen += linesize;
		}

		if (c == '\\' && tagged == 0) {
			tagged = 1;
			continue;
		}

		if (tagged == 1) {
			switch (c) {
				case 'n' : 
					line->buffer[pos++] = '\n';
					break;

				case 'r' : 
					line->buffer[pos++] = '\r';
					break;

				case 't' : 
					line->buffer[pos++] = '\t';
					break;

				case 'b' : 
					line->buffer[pos++] = '\b';
					break;

				case '\'' : 
					line->buffer[pos++] = '\'';
					line->buffer[pos++] = '\'';
					break;

				case '\"' : 
					line->buffer[pos++] = '\"';
					break;

				case '\\' : 
					line->buffer[pos++] = '\\';
					break;

				case '%' : 
					line->buffer[pos++] = '%';
					break;

				case '_' : 
					line->buffer[pos++] = '_';
					break;
			}

			tagged = 0;
			continue;
		}

		if (c == '\n' || c == '\r')
			break;

		tagged = 0;
		line->buffer[pos++] = c;
	}

	if (pos == 0 && c == -1)
		return NULL;

	line->buffer[pos] = '\0';
	line->len = pos;

	return line;
}

/* Import MySQL dumps created with:
mysqldump --skip-disable-keys --skip-add-locks --skip-add-drop-table \
          --compatible=mssql --complete-insert --single-transaction \
          --no-create-db --no-create-info --quick --skip-quote-names \
          --skip-extended-insert -p <database> <tablename> | gzip > tablename.sql.gz
*/
void import_sqldump ()
{
	uint64_t success = 0, failed = 0, lines = 0;
	struct linebuffer b = {NULL, 0, 0};
	gzFile f;

	if (strcmp (config.import_fname, "-") == 0)
		f = gzdopen (STDIN_FILENO, "r");
	else
		f = gzopen (config.import_fname, "r");

	if (f == NULL) {
		write_to_log(LOG_CRIT,"Error opening file: %s", config.import_fname);
		return;
	}

	while (read_linebuffer (f, &b) != NULL) {
		if (b.len < 3)
			continue;

		if (memcmp (b.buffer, "--", 2) == 0 || memcmp (b.buffer, "/*", 2) == 0)
			continue;

		if (1 != modfunc.executesql (queues[0].modhandle, b.buffer)) {
			char *p = modfunc.geterrorstring(queues[0].modhandle);

			printf ("'%s' DB:%s\n", b.buffer, p == NULL ? "Unknown error" : p);
			failed++;
		} else {
			success++;
		}

		if(++lines % 1000 == 0)
			fprintf(stderr, "\rSuccess:%" PRIu64 " Failed:%" PRIu64, success, failed);
	}

	if (b.buffer != NULL)
		free (b.buffer);

	gzclose (f);
	fprintf(stderr, "\rSuccess:%" PRIu64 " Failed:%" PRIu64, success, failed);
}

