/******************************************************************************
* 
* CAEN SpA - Front End Division
* Via Vetraia, 11 - 55049 - Viareggio ITALY
* +390594388398 - www.caen.it
*
***************************************************************************//**
* \note TERMS OF USE:
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation. This program is distributed in the hope that it will be useful, 
* but WITHOUT ANY WARRANTY; without even the implied warranty of 
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. The user relies on the 
* software, documentation and results solely at his own risk.
******************************************************************************/


#include <stdio.h>
#include <ctype.h>
#include "paramparser.h"
#include "JanusC.h"
#include "console.h"
#include "FERSutils.h"
//#include "FERSlib.h"


#define SETBIT(r, m, b)  (((r) & ~(m)) | ((m) * (b)))

int ValidParameterName = 0;
int ValidParameterValue = 0;
int ValidUnits = 0;

// ---------------------------------------------------------------------------------
// Description: compare two strings
// Inputs:		str1, str2: strings to compare
// Outputs:		-
// Return:		1=equal, 0=not equal
// ---------------------------------------------------------------------------------
int streq(char *str1, char *str2)
{
	if (strcmp(str1, str2) == 0) {
		ValidParameterName = 1;
		return 1;
	} else {
		return 0;
	}
}


// ---------------------------------------------------------------------------------
// Description: Trim a string left and right
// Inputs:		string to be trimmed
// Outputs:		string trimmed
// Return:		string trimmed
// ---------------------------------------------------------------------------------
char* ltrim(char* s) {
	while (isspace(*s)) s++;
	return s;
}

char* rtrim(char* s) {
	char* back = s + strlen(s) - 1;
	while (isspace(*back)) --back;
	*(back + 1) = '\0';
	return s;
}

char* trim(char* s) {
	return rtrim(ltrim(s));
}


// ---------------------------------------------------------------------------------
// Description: check if the directory exists, if not it is created
// Inputs:		J_cfg, Cfg file
// Outputs:		-
// Return:		void
// ---------------------------------------------------------------------------------
int f_mkdir(const char* path) {	// taken from CAENMultiplatform.c (https://gitlab.caen.it/software/utility/-/blob/develop/src/CAENMultiplatform.c#L216)
	int32_t ret = 0;
#ifdef _WIN32
	DWORD r = (CreateDirectoryA(path, NULL) != 0) ? 0 : GetLastError();
	switch (r) {
	case 0:
	case ERROR_ALREADY_EXISTS:
		ret = 0;
		break;
	default:
		ret = -1;
		break;
	}
#else
	int r = mkdir(path, ACCESSPERMS) == 0 ? 0 : errno;
	switch (r) {
	case 0:
	case EEXIST:
		ret = 0;
		break;
	default:
		ret = -1;
		break;
	}
#endif
	return ret;
}

void GetDatapath(char* value, Janus_Config_t* J_cfg) {

	if (access(value, 0) == 0) { // taken from https://stackoverflow.com/questions/6218325/
		struct stat status;
		stat(value, &status);
		int myb = status.st_mode & S_IFDIR;
		if (myb == 0) {
			Con_printf("LCSw", "WARNING: DataFilePath: %s is not a valid directory. Default .DataFiles folder is used\n", value);
			strcpy(J_cfg->DataFilePath, "DataFiles");
		} else
			strcpy(J_cfg->DataFilePath, value);
	} else {
		int ret = f_mkdir(value);
		if (ret == 0)
			strcpy(J_cfg->DataFilePath, value);
		else {
			Con_printf("LCSw", "WARNING: DataFilePath: %s cannot be created, default .DataFiles folder is used\n", value);
			strcpy(J_cfg->DataFilePath, "DataFiles");
		}
	}
}

// ---------------------------------------------------------------------------------
// Description: Read an integer (decimal) from the conig file
// Inputs:		f_ini: config file
// Outputs:		-
// Return:		integer value read from the file / Set ValidParameterValue=0 if the string is not an integer
// ---------------------------------------------------------------------------------
static uint32_t GetInt(char* val)
{
	uint32_t ret;
	int num = sscanf(val, "%d", &ret);
	if (ret < 0 || num < 1) ValidParameterValue = 0;
	else ValidParameterValue = 1;
	return ret;
}


// ---------------------------------------------------------------------------------
// Description: Read number of bins for amn histogram
// Inputs:		f_ini: config file
// Outputs:		-
// Return:		integer value read from the file
// ---------------------------------------------------------------------------------
static int GetNbin(char* str)
{
	int i;
	for (i=0; i<(int)strlen(str); i++)
		str[i] = toupper(str[i]);
	if		((streq(str, "DISABLED")) || (streq(str, "0")))	return 0;
	else if  (streq(str, "256"))							return 256;
	else if  (streq(str, "512"))							return 512;	
	else if ((streq(str, "1024")  || streq(str, "1K")))		return 1024;
	else if ((streq(str, "2048")  || streq(str, "2K")))		return 2048;
	else if ((streq(str, "4096")  || streq(str, "4K")))		return 4096;
	else if ((streq(str, "8192")  || streq(str, "8K")))		return 8192;
	else if ((streq(str, "16384") || streq(str, "16K")))	return 16384;
	else { 												
		ValidParameterValue = 0;
		return 1024;  // assign a default value on error
		//Con_printf("LCSw", "WARNING: Einvalid Nbin value %s\n", str);
	}
}


// ---------------------------------------------------------------------------------
// Description: Read an unsigned integer 32 bits (hexadecimal) from the conig file
// Inputs:		f_ini: config file
// Outputs:		-
// Return:		unsigned integer 32 bits value read from the file / Set ValidParameterValue = 0 if the value is not in HEX format
// ---------------------------------------------------------------------------------
static uint32_t GetHex32(char *str)
{ 
	uint32_t ret;
	ValidParameterValue = 1;
	if ((str[1] == 'x') || (str[1] == 'X')) {
		sscanf(str + 2, "%x", &ret);
		if (str[0] != '0') ValidParameterValue = 0;	// Rise a warning for wrong HEX format 0x
		for (uint8_t i = 2; i < strlen(str); ++i) {
			if (!isxdigit(str[i])) {
				ValidParameterValue = 0;
				break;
			}
		}
	}
	else {
		sscanf(str, "%x", &ret);
		for (uint8_t i = 0; i < strlen(str); ++i) {	// Rise a warning for wrong HEX format
			if (!isxdigit(str[i])) {
				ValidParameterValue = 0;
				break;
			}
		}
	}
	return ret;
}


// ---------------------------------------------------------------------------------
// Description: Read an unsigned integer 64 bits (hexadecimal) from the conig file
// Inputs:		f_ini: config file
// Outputs:		-
// Return:		unsigned integer 64 bits value read from the file / Set ValidParameterValue = 0 if the value is not in HEX format
// ---------------------------------------------------------------------------------
static uint64_t GetHex64(char* str) {
	uint64_t ret;
	ValidParameterValue = 1;
	if ((str[1] == 'x') || (str[1] == 'X')) {
		sscanf(str + 2, "%" SCNx64, &ret);
		if (str[0] != '0') ValidParameterValue = 0;	// Rise a warning for wrong HEX format 0x
		for (uint8_t i = 2; i < strlen(str); ++i) {
			if (!isxdigit(str[i])) {
				ValidParameterValue = 0;
				break;
			}
		}
	} else {
		sscanf(str, "%" SCNx64, &ret);
		for (uint8_t i = 0; i < strlen(str); ++i) {	// Rise a warning for wrong HEX format
			if (!isxdigit(str[i])) {
				ValidParameterValue = 0;
				break;
			}
		}
	}
	return ret;
}


// ---------------------------------------------------------------------------------
// Description: Read a float from the conig file
// Inputs:		f_ini: config file
// Outputs:		-
// Return:		float value read from the file / 
// ---------------------------------------------------------------------------------
static float GetFloat(char *str)
{
	float ret;
	int i;
	ValidParameterValue = 1;
	// replaces ',' with '.' (decimal separator)
	for(i=0; i<(int)strlen(str); i++)
		if (str[i] == ',') str[i] = '.';
	sscanf(str, "%f", &ret);
	return ret;
}


// ---------------------------------------------------------------------------------
// Description: Read a value from the conig file followed by an optional time unit (ps, ns, us, ms, s)
//              and convert it in a time expressed in ns as a float 
// Inputs:		f_ini: config file
//				tu: time unit of the returned time value
// Outputs:		-
// Return:		time value (in ns) read from the file / Set ValidParamName/Value=0 if the expected format is not matched
// ---------------------------------------------------------------------------------
static float GetTime(char *val, char *tu)
{
	double timev=-1;
	double ns;
	char str[100];

	int element = sscanf(val, "%lf %s", &timev, str);
 
	ValidUnits = 1;
	if (streq(str, "ps"))		ns = timev * 1e-3;
	else if (streq(str, "ns"))	ns = timev;
	else if (streq(str, "us"))	ns = timev * 1e3;
	else if (streq(str, "ms"))	ns = timev * 1e6;
	else if (streq(str, "s"))	ns = timev * 1e9;
	else if (streq(str, "m"))	ns = timev * 60e9;
	else if (streq(str, "h"))	ns = timev * 3600e9;
	else if (streq(str, "d"))	ns = timev * 24 * (3600e9);
	else if (element == 1 || streq(str, "#")) return (float)timev;
	else {
		ValidUnits = 0;
		return (float)timev;  // no time unit specified in the config file; assuming equal to the requested one (ns of default)
	}

	if (streq(tu, "ps"))		return (float)(ns*1e3);
	else if (streq(tu, "ns"))	return (float)(ns);
	else if (streq(tu, "us"))	return (float)(ns/1e3);
	else if (streq(tu, "ms"))	return (float)(ns/1e6);
	else if (streq(tu, "s") )	return (float)(ns/1e9);
	else if (streq(tu, "m") )	return (float)(ns/60e9);
	else if (streq(tu, "h") )	return (float)(ns/3600e9);
	else if (streq(tu, "d") )	return (float)(ns/(24*3600e9));
	else return (float)timev;
}


// ---------------------------------------------------------------------------------
// Description: Read a value from the conig file followed by an optional byte unit (B, MB, GB)
//              and convert it in Bytes
// Inputs:		f_ini: config file
// Outputs:		-
// Return:		memory size value expressed in Bytes. Minimum value allowed 1 kB
// ---------------------------------------------------------------------------------
static float GetBytes(char* val)
{
	char van[50];
	float var;
	char str[100];
	float minSize = 1e3; // 1 kB

	int element0 = sscanf(val, "%s %s", van, str);
	int element1 = sscanf(val, "%f", &var);
	if (element1 > 0) ValidParameterValue = 1;
	else ValidParameterValue = 0;

	ValidUnits = 1;
	if (streq(str, "TB"))		return (var * 1e12 > minSize) ? (float)(var * 1e12) : minSize;
	else if (streq(str, "GB"))	return (var * 1e9 > minSize)  ? (float)(var * 1e9) : minSize;
	else if (streq(str, "MB"))	return (var * 1e6 > minSize)  ? (float)(var * 1e6) : minSize;
	else if (streq(str, "kB"))	return (var * 1e3 > minSize)  ? (float)(var * 1e3) : minSize;
	else if (streq(str, "B"))	return (var > minSize) ? var : minSize;
	else if (element0 * element1 == 1 || streq(str, "#")) {	// no units, assumed Byte
		return (var > 1e6) ? var : minSize;
	} else {	// wrong units, raise warning
		ValidUnits = 0;
		return (var > minSize) ? var : minSize;  // no units specified in the config file; assuming bytes
	}
}


static void SetChannelParam8(int handle, uint8_t param[MAX_NBRD][FERSLIB_MAX_NCH_5203], uint16_t val, int ch) {
	int i;
	for (int tb = 0; tb < MAX_NBRD_JANUS; ++tb) {
		if (ch == -1) {
			for (i = 0; i < FERS_NumChannels(handle); i++)
				param[tb][i] = val;
		} else {
			param[tb][ch] = val;
		}
	}
}


static void SetLibParameters(char *parname, char *parval, int ch, int brd_l, int brd_h) {
	int ret = 0;
	if (!ValidParameterName) {
		// Append the '[ch]' if ch != 0
		if (ch >= 0)
			sprintf(parname, "%s[%d]", parname, ch);
		for (int b = brd_l; b < brd_h; b++) {
			int ret;
			//printf("%s %s\n", parname, parval);
			ret = FERS_SetParam(handle[b], parname, parval);
			ValidParameterName = (ret == FERSLIB_ERR_INVALID_PARAM) ? 0 : 1;
			ValidParameterValue = (ret == FERSLIB_ERR_INVALID_PARAM_VALUE) ? 0 : 1;
			ValidUnits = (ret == FERSLIB_ERR_INVALID_PARAM_UNIT) ? 0 : 1;
		}
	}
}


static void LoadMacro(char *parval, Janus_Config_t *J_cfg2, int ParsingMode) {
	FILE* n_cfg;
	n_cfg = fopen(parval, "r");
	if (n_cfg != NULL) {
		Con_printf("LCSm", "Loading Additional config file \"%s\"\n", parval);
		ParseConfigFile(n_cfg, J_cfg2, ParsingMode & 0xE);
		fclose(n_cfg);
	} else {
		Con_printf("LCSw", "WARNING: Loading Macro: Macro file \"%s\" not found\n", parval);
		ValidParameterValue = 0;
	}
}


// ---------------------------------------------------------------------------------
// Description: Read a config file, parse the parameters and set the relevant fields in the J_cfg structure
// Inputs:		f_ini: config file pinter
// Outputs:		J_cfg: struct with all parameters
// Return:		0=OK, -1=error
// ---------------------------------------------------------------------------------
int ParseConfigFile(FILE* f_ini, Janus_Config_t* J_cfg, int ParseMode)
{
	int i, b; // , tr = -1;
	int brd, ch;  // target board/ch defined as ParamName[b][ch] (-1 = all)
	int brd_l, brd_h;  // low/high index for multiboard settings
	char tstr[1000], str1[1000], * parval, * tparval, * parname, * token;

	if ((ParseMode & PARSEMODE_PARSE_CONNECTION) & PARSEMODE_FIRST_CALL) {
		J_cfg->NumBrd = 0;

	}

	if (ParseMode & PARSEMODE_FIRST_CALL) { // initialize J_cfg when it is call by Janus. No when it is call for overwrite parameters 
		int num_brd = J_cfg->NumBrd;
		char mpath[MAX_NBRD][200];
		for (int i = 0; i < MAX_NBRD_JANUS; ++i)
			sprintf(mpath[i], "%s", J_cfg->ConnPath[i]);

		memset(J_cfg, 0, sizeof(Janus_Config_t));

		/* Default settings */
		strcpy(J_cfg->DataFilePath, "DataFiles");
		J_cfg->NumBrd = num_brd;
		for (int i = 0; i < MAX_NBRD_JANUS; ++i)
			sprintf(J_cfg->ConnPath[i], "%s", mpath[i]);

		J_cfg->NumCh = 64;
		J_cfg->LeadTrailHistoNbin = 4096;
		J_cfg->ToTHistoNbin = 4096;
		J_cfg->LeadTrailRebin = 1;
		J_cfg->ToTHistoMin = 0;
		J_cfg->ToTRebin = 1;
		J_cfg->AcquisitionMode = 0;
		J_cfg->GateWidth = -500;
		for (i = 0; i < 6; i++)
			J_cfg->WalkFitCoeff[i] = (i == 1) ? (float)1 : (float)0;
		J_cfg->En_128_ch = 0;
		J_cfg->En_service_event = 1;
		J_cfg->DataAnalysis = 0xFFFF; // enable all
		J_cfg->MaxOutFileSize = 1e9; // 1 GB
		J_cfg->EnableRawDataRead = 0;
		J_cfg->EnableMaxFileSize = 0;
		for (int bb = 0; bb < MAX_NBRD_JANUS; ++bb) {
			for (int cc = 0; cc < FERSLIB_MAX_NCH_5203; ++cc) {
				J_cfg->InvertEdgePolarity[bb][cc] = 0;
			}
		}

	}

	//read config file and assign parameters 
	while (!feof(f_ini)) {
		brd = -1;
		ch = -1;
		ValidParameterName = 0;
		ValidParameterValue = 1;
		ValidUnits = 1;

		// Read a line from the file
		fgets(tstr, sizeof(tstr), f_ini);
		if (strlen(tstr) < 2) continue;

		// skip comments
		if (tstr[0] == '#' || strlen(tstr) <= 2) continue;
		if (strstr(tstr, "#") != NULL) tparval = strtok(tstr, "#");
		else tparval = tstr;

		// Get param name (str) and values (parval)
		sscanf(tparval, "%s", str1);
		tparval += strlen(str1);
		parval = trim(tparval);

		// Search for boards and channels
		parname = strtok(trim(str1), "[]"); // Param name with [brd][ch]
		token = strtok(NULL, "[]");
		if (token != NULL) {
			sscanf(token, "%d", &brd);
			if ((token = strtok(NULL, "[]")) != NULL)
				sscanf(token, "%d", &ch);
		}
		if (brd != -1) {
			brd_l = brd;
			brd_h = brd + 1;
		} else {
			brd_l = 0;
			brd_h = (J_cfg->NumBrd > 0) ? J_cfg->NumBrd : MAX_NBRD_JANUS;
		}

		if (brd_l < 0 || brd_l > (MAX_NBRD_JANUS - 1)) { // brd_l cannot be larger than FERSLIB_MAX_NBRD - 1
			Con_printf("LCSw", "%s: brd %d index out of range\n", parname, brd_l);
			continue;
		}

		// Some name replacement for back compatibility
		if (streq(parname, "BunchTrgSource"))		sprintf(parname, "TriggerSource");
		if (streq(parname, "DwellTime"))			sprintf(parname, "PtrgPeriod");
		if (streq(parname, "TrgTimeWindow"))		sprintf(parname, "TstampCoincWindow");
		if (streq(parname, "En_service_event"))		sprintf(parname, "EnableServiceEvents");

		// START PARAM PARSER
		if (ParseMode & PARSEMODE_PARSE_CONNECTION) {
			if (streq(parname, "Open")) {
				if (brd == -1) {
					Con_printf("LCSw", "%s: cannot be a common setting (must be in a [BOARD] section)\n", parname);
				} else {
					if (J_cfg->NumBrd >= MAX_NBRD_JANUS) {
						Con_printf("LCSw", "WARNING: Maximum number of supported boards reached (NumBoards = %d)\n", J_cfg->NumBrd);
						continue;
					}
					if (streq(J_cfg->ConnPath[brd], ""))
						J_cfg->NumBrd++;
					strcpy(J_cfg->ConnPath[brd], parval);

					if (streq(J_cfg->ConnPath[brd], ""))
						Con_printf("LCSw", "%s: connection path cannot be empty\n", parval);
				}
			}
			// Also FiberDelayAdjust can be read and used in Lib during InitTdlChains
			if (streq(parname, "Load")) 
				LoadMacro(parval, J_cfg, ParseMode);
			if (streq(parname, "FiberDelayAdjust")) {
				int np, cnc, chain, node;
				float length; // length expressed in m
				np = sscanf(parval, "%d %d %d %f", &cnc, &chain, &node, &length);
				if ((np == 4) && (cnc >= 0) && (cnc < FERSLIB_MAX_NCNC) && (chain >= 0) && (chain < 8) && (node >= 0) && (node < 16))
					J_cfg->FiberDelayAdjust[cnc][chain][node] = length;
			}
			continue; // Skip the rest of the file
		}

		if (streq(parname, "Open")) continue;  // Skip Open param when no PARSE_CONNECTION

		//if (streq(parname, "En_service_event")) {
		//	if (streq(parval, "DISABLED") || streq(parval, "0"))		J_cfg->En_service_event = 0;
		//	else if (streq(parval, "ENABLED") || streq(parval, "1"))	J_cfg->En_service_event = 1;
		//	else 	ValidParameterValue = 0;
		//}
		if (streq(parname, "En_128_ch")) {
			if (streq(parval, "DISABLED"))			J_cfg->En_128_ch = 0;
			else if (streq(parval, "ENABLED"))		J_cfg->En_128_ch = 1;
			else 	ValidParameterValue = 0;
		}
		if (streq(parname, "EventBuildingMode")) {
			if (streq(parval, "DISABLED"))				J_cfg->EventBuildingMode = EVBLD_DISABLED;
			else if (streq(parval, "TRGTIME_SORTING"))	J_cfg->EventBuildingMode = EVBLD_TRGTIME_SORTING;
			else if (streq(parval, "TRGID_SORTING"))	J_cfg->EventBuildingMode = EVBLD_TRGID_SORTING;
			else 	ValidParameterValue = 0;
		}
		if (streq(parname, "DataAnalysis")) {
			if (streq(parval, "NONE") || streq(str1, "DISABLED"))	J_cfg->DataAnalysis = 0;
			else if (streq(parval, "CNT_ONLY"))		J_cfg->DataAnalysis = DATA_ANALYSIS_CNT;
			else if (streq(parval, "CNT+MEAS"))		J_cfg->DataAnalysis = DATA_ANALYSIS_CNT | DATA_ANALYSIS_MEAS;
			else if (streq(parval, "CNT+HISTO"))		J_cfg->DataAnalysis = DATA_ANALYSIS_CNT | DATA_ANALYSIS_HISTO;
			else if (streq(parval, "ALL"))			J_cfg->DataAnalysis = DATA_ANALYSIS_CNT | DATA_ANALYSIS_HISTO | DATA_ANALYSIS_MEAS;
			else if (strstr(parval, "MASK"))			J_cfg->DataAnalysis = GetHex32(trim(strtok(parval, "MASK")));
			else 	ValidParameterValue = 0;
		}
		if (streq(parname, "OF_OutFileUnit")) {
			if (streq(parval, "LSB"))					J_cfg->OutFileUnit = OF_UNIT_LSB;
			else if (streq(parval, "ns"))				J_cfg->OutFileUnit = OF_UNIT_NS;
			else 	ValidParameterValue = 0;
		}

		if (streq(parname, "DataFilePath"))				GetDatapath(parval, J_cfg);
		if (streq(parname, "LeadTrailHistoNbin"))		J_cfg->LeadTrailHistoNbin = GetNbin(parval);
		if (streq(parname, "ToTHistoNbin"))				J_cfg->ToTHistoNbin = GetNbin(parval);
		if (streq(parname, "LeadHistoMin"))				J_cfg->LeadHistoMin = GetTime(parval, "ns");
		if (streq(parname, "TrailHistoMin"))			J_cfg->TrailHistoMin = GetTime(parval, "ns");
		if (streq(parname, "LeadTrailRebin"))			J_cfg->LeadTrailRebin = GetInt(parval);
		if (streq(parname, "ToTHistoMin"))				J_cfg->ToTHistoMin = GetTime(parval, "ns");
		if (streq(parname, "ToTRebin"))					J_cfg->ToTRebin = GetInt(parval);
		//if (streq(parname, "ToT_LSB"))					J_cfg->ToT_LSB = GetInt(parval);
		//if (streq(parname, "LeadTrail_LSB"))			J_cfg->LeadTrail_LSB = GetInt(parval);
		if (streq(parname, "EnableWalkCorrection"))		J_cfg->EnableWalkCorrection = GetInt(parval);

		if (streq(parname, "JobFirstRun"))				J_cfg->JobFirstRun = GetInt(parval);
		if (streq(parname, "JobLastRun"))				J_cfg->JobLastRun = GetInt(parval);
		if (streq(parname, "RunSleep"))					J_cfg->RunSleep = GetTime(parval, "s");
		if (streq(parname, "EnableJobs"))				J_cfg->EnableJobs = GetInt(parval);

		if (streq(parname, "OF_EnMaxSize"))				J_cfg->EnableMaxFileSize = GetInt(parval);
		if (streq(parname, "OF_MaxSize"))				J_cfg->MaxOutFileSize = GetBytes(parval);
		if (streq(parname, "OutFileEnableMask"))		J_cfg->OutFileEnableMask = GetHex32(parval);
		//if (streq(str, "OF_RawBin"))				J_cfg->OutFileEnableMask	= SETBIT(J_cfg->OutFileEnableMask, OUTFILE_RAW_DATA_BIN, GetInt(f_ini));
		//if (streq(str, "OF_RawAscii"))				J_cfg->OutFileEnableMask	= SETBIT(J_cfg->OutFileEnableMask, OUTFILE_RAW_DATA_ASCII, GetInt(f_ini));
		//if (streq(parname, "OF_RawData"))				J_cfg->OutFileEnableMask = SETBIT(J_cfg->OutFileEnableMask, OUTFILE_RAW_LL, GetInt(parval)); // Lib parameter
		if (streq(parname, "OF_ListBin"))				J_cfg->OutFileEnableMask = SETBIT(J_cfg->OutFileEnableMask, OUTFILE_LIST_BIN, GetInt(parval));
		if (streq(parname, "OF_ListCSV"))				J_cfg->OutFileEnableMask = SETBIT(J_cfg->OutFileEnableMask, OUTFILE_LIST_CSV, GetInt(parval));
		if (streq(parname, "OF_ListAscii"))				J_cfg->OutFileEnableMask = SETBIT(J_cfg->OutFileEnableMask, OUTFILE_LIST_ASCII, GetInt(parval));
		if (streq(parname, "OF_Sync"))					J_cfg->OutFileEnableMask = SETBIT(J_cfg->OutFileEnableMask, OUTFILE_SYNC, GetInt(parval));
		if (streq(parname, "OF_LeadHisto"))				J_cfg->OutFileEnableMask = SETBIT(J_cfg->OutFileEnableMask, OUTFILE_LEAD_HISTO, GetInt(parval));
		if (streq(parname, "OF_TrailHisto"))			J_cfg->OutFileEnableMask = SETBIT(J_cfg->OutFileEnableMask, OUTFILE_TRAIL_HISTO, GetInt(parval));
		if (streq(parname, "OF_ToTHisto"))				J_cfg->OutFileEnableMask = SETBIT(J_cfg->OutFileEnableMask, OUTFILE_TOT_HISTO, GetInt(parval));
		if (streq(parname, "OF_LeadMeas"))				J_cfg->OutFileEnableMask = SETBIT(J_cfg->OutFileEnableMask, OUTFILE_LEAD_MEAS, GetInt(parval));
		if (streq(parname, "OF_ServiceInfo"))			J_cfg->OutFileEnableMask = SETBIT(J_cfg->OutFileEnableMask, OUTFILE_SERVICE_INFO, GetInt(parval));
		//if (streq(str, "OF_TrailMeas"))				J_cfg->OutFileEnableMask	= SETBIT(J_cfg->OutFileEnableMask, OUTFILE_TRAIL_MEAS, GetInt(f_ini));
		if (streq(parname, "OF_ToTMeas"))				J_cfg->OutFileEnableMask = SETBIT(J_cfg->OutFileEnableMask, OUTFILE_TOT_MEAS, GetInt(parval));
		if (streq(parname, "OF_RunInfo"))				J_cfg->OutFileEnableMask = SETBIT(J_cfg->OutFileEnableMask, OUTFILE_RUN_INFO, GetInt(parval));
		if (streq(parname, "TstampCoincWindow"))		J_cfg->TstampCoincWindow = (uint32_t)GetTime(parval, "ns");
		if (streq(parname, "PresetTime"))				J_cfg->PresetTime = GetTime(parval, "s");
		if (streq(parname, "PresetCounts"))				J_cfg->PresetCounts = GetInt(parval);
		if (streq(parname, "RunNumber_AutoIncr"))		J_cfg->RunNumber_AutoIncr = GetInt(parval);
		if (streq(parname, "InvertEdgePolarity"))		SetChannelParam8(handle[b], J_cfg->InvertEdgePolarity, uint8_t(GetInt(parval)), ch);

		if (streq(parname, "Load")) {
			LoadMacro(parval, J_cfg, ParseMode);
			//FILE* n_cfg;
			//n_cfg = fopen(parval, "r");
			//if (n_cfg != NULL) {
			//	Con_printf("LCSm", "Loading Additional config file \"%s\"\n", parval);
			//	ParseConfigFile(n_cfg, J_cfg, ParseMode & 0xE);
			//	fclose(n_cfg);
			//} else {
			//	Con_printf("LCSw", "WARNING: Loading Macro: Macro file \"%s\" not found\n", parval);
			//	ValidParameterValue = 0;
			//}
		}

		if (streq(parname, "WalkFitCoeff")) {
			float c[6] = { 0 };
			int nc;
			fgets(str1, 500, f_ini);
			nc = sscanf(str1, "%f %f %f %f %f %f", &c[0], &c[1], &c[2], &c[3], &c[4], &c[5]);
			for (i = 0; (i < nc) && (i < 6); i++)
				J_cfg->WalkFitCoeff[i] = c[i];
		}

		// Param name not find in Janus param list. Can be a param of the lib. Call the set_param
		if (ParseMode & PARSEMODE_PARSE_ALL) {
			SetLibParameters(parname, parval, ch, brd_l, brd_h);
			//if (!ValidParameterName) {
			//	// Append the '[ch]' if ch != 0
			//	if (ch >= 0)
			//		sprintf(parname, "%s[%d]", parname, ch);
			//	for (b = brd_l; b < brd_h; b++) {
			//		int ret;
			//		//printf("%s %s\n", parname, parval);
			//		ret = FERS_SetParam(handle[b], parname, parval);
			//		ValidParameterName = (ret == FERSLIB_ERR_INVALID_PARAM) ? 0 : 1;
			//		ValidParameterValue = (ret == FERSLIB_ERR_INVALID_PARAM_VALUE) ? 0 : 1;
			//		ValidUnits = (ret == FERSLIB_ERR_INVALID_PARAM_UNIT) ? 0 : 1;
			//	}
			//}
		}

		if (!ValidParameterName)
			Con_printf("LCSw", "WARNING: %s: unkwown parameter\n", parname);
		else if (!ValidParameterValue)
			Con_printf("LCSw", "WARNING: %s: unkwown value '%s'\n", parname, parval);
		else if (!ValidUnits)
			Con_printf("LCSw", "WARNING: %s: unkwown units. Janus use as default V, mA, ns\n", parname);
	}

	if (!(ParseMode & PARSEMODE_FIRST_CALL)) return 0; // The following code have to be run just once, in the first call of paramparser.c

	J_cfg->AcquisitionMode	= FERS_GetParam_int(handle[0], "AcquisitionMode");
	J_cfg->MeasMode			= FERS_GetParam_int(handle[0], "MeasMode");
	J_cfg->StartRunMode		= FERS_GetParam_int(handle[0], "StartRunMode");
	J_cfg->StopRunMode		= FERS_GetParam_int(handle[0], "StopRunMode");
	J_cfg->En_service_event = FERS_GetParam_int(handle[0], "EnableServiceEvents");
	J_cfg->En_128_ch		= FERS_NumChannels(handle[0]) == 128 ? 1 : 0;
	J_cfg->LeadTrail_LSB	= FERS_GetParam_uint32(handle[0], "LeadTrail_LSB");
	J_cfg->ToT_LSB			= FERS_GetParam_uint32(handle[0], "ToT_LSB");
	J_cfg->GateWidth		= FERS_GetParam_float(handle[0], "GateWidth");
	
	// If the adapter is selected, check if it is actually connected
	J_cfg->AdapterType = FERS_GetParam_int(handle[0], "AdapterType");
	if (J_cfg->AdapterType != ADAPTER_NONE && !offline_conn) {
		int ret = 0;
		for (int bb = 0; bb < J_cfg->NumBrd; ++bb) {
			FERS_A5256_Info_t temp; 
			ret |= FERS_checkA5256presence(handle[bb], &temp);
		}
		if (ret < 0) {
			Con_printf("CSw", "No A5256 adapter found. Switch to None adapter\n");
			Con_printf("SM", "AdapterType:%d", ADAPTER_NONE);
			for (int bb = 0; bb < J_cfg->NumBrd; ++bb) {
				FERS_SetParam(handle[bb], "AdapterType", "NONE");
			}
			J_cfg->AdapterType = FERS_GetParam_int(handle[0], "AdapterType");
		}
	}	J_cfg->TestMode = FERS_GetParam_int(handle[0], "TestMode");
	
	// Use  in CStart/Stop
	//if (J_cfg->AcquisitionMode == ACQMODE_COMMON_START || J_cfg->AcquisitionMode == ACQMODE_COMMON_STOP)
	//	J_cfg->TrgWindowWidth = J_cfg->GateWindowWidth;

	// Enable/Disable Histograms - implemented for back compatibility of Janus code
	if (!(J_cfg->DataAnalysis & DATA_ANALYSIS_HISTO)) {
		J_cfg->LeadTrailHistoNbin = 0;
		J_cfg->ToTHistoNbin = 0;
	}

	if (MEASMODE_OWLT(J_cfg->MeasMode)) {
		J_cfg->LeadTrail_Rescale = 0;
		J_cfg->ToT_Rescale = 0;
	} else {
		J_cfg->LeadTrail_Rescale = J_cfg->LeadTrail_LSB;
		J_cfg->ToT_Rescale = J_cfg->ToT_LSB;
	}
	J_cfg->LeadTrail_LSB_ns = (float)(TDC_LSB_ps * (1 << J_cfg->LeadTrail_LSB)) / 1000.;
	J_cfg->ToT_LSB_ns = (float)(TDC_LSB_ps * (1 << J_cfg->ToT_LSB)) / 1000.;
	//if (J_cfg->MeasMode == MEASMODE_LEAD_TRAIL) J_cfg->ToT_LSB_ns = J_cfg->LeadTrail_LSB_ns;  // In Lead Trail, the ToT is Lead-Trail, thus the resolution is the same of LEAD measurement
	//else J_cfg->ToT_LSB_ns = (float)(TDC_LSB_ps * (1 << J_cfg->ToT_LSB)) / 1000.;

	// Warning that LEAD_TOT cannot be used in STREAMING
	if (J_cfg->AcquisitionMode == ACQMODE_STREAMING) {
		if (J_cfg->MeasMode == MEASMODE_LEAD_TOT8 || J_cfg->MeasMode == MEASMODE_LEAD_TOT11) {
			Con_printf("LCSw", "WARNING: Meas Mode: LEAD+TOT modes cannot be used in STREAMING. MeasMode switched to LEAD_ONLY\n");
			J_cfg->MeasMode = MEASMODE_LEAD_ONLY;
		}
	}

	// Warning if TrgWindow(GateWindow) > DynamicRange ToA, to avoid ToA rollOver
	int HeadTrail = FERS_GetParam_int(handle[0], "En_Head_Trail");
	int two_bit = HeadTrail == 1 || HeadTrail == 2? 0: 1;
	float GateTrgWindow = 0;
	char pName[100];
	//if (J_cfg->En_Head_Trail == 1 || J_cfg->En_Head_Trail == 2) two_bit = 0;
	//else two_bit = 1;
	if (J_cfg->AcquisitionMode == ACQMODE_COMMON_START || J_cfg->AcquisitionMode == ACQMODE_COMMON_STOP) {
		GateTrgWindow = J_cfg->GateWidth;
		sprintf(pName, "GateWidth");
	} else {
		GateTrgWindow = FERS_GetParam_float(handle[0], "TrgWindowWidth");
		sprintf(pName, "TrgWindowWidth");
	}

	float gate_hl = 0;
	if (J_cfg->MeasMode == MEASMODE_LEAD_ONLY || J_cfg->MeasMode == MEASMODE_LEAD_TRAIL)
		gate_hl = (1 << (24 + 2 * two_bit)) * (J_cfg->LeadTrail_LSB_ns);
	else if (J_cfg->MeasMode == MEASMODE_LEAD_TOT8)
		gate_hl = (1 << (17 + 2 * two_bit)) * (J_cfg->LeadTrail_LSB_ns);
	else if (J_cfg->MeasMode == MEASMODE_LEAD_TOT11)
		gate_hl = (1 << (14 + 2 * two_bit)) * (J_cfg->LeadTrail_LSB_ns);

	if (GateTrgWindow > gate_hl) {
		Con_printf("LCSw", "WARNING: %s: larger than Lead dynamic range. Value set to %.1f ns\n", pName, gate_hl);
		char gate_val[50];
		sprintf(gate_val, "%.0f", gate_hl);
		for (int bb = 0; b < J_cfg->NumBrd; ++bb) FERS_SetParam(handle[b], pName, gate_val);
		Con_printf("SM", "%s:%s", pName, gate_val);
	}

	// Warning that StopRun cannot be MANUAL if Job are enabled
	if (J_cfg->EnableJobs && (J_cfg->StopRunMode == STOPRUN_MANUAL)) {
		Con_printf("LCSw", "WARNING: StopRunMode: Cannot configure STOPRUN MANUAL during jobs. Switching to PRESET COUNT\n");
		J_cfg->StopRunMode = STOPRUN_PRESET_COUNTS;
	}

	// Avoid plots to show beyond the histogramMax
	//if (J_cfg->LeadTrailHistoNbin > (1 << TOA_NBIT))	J_cfg->LeadTrailHistoNbin = (1 << TOA_NBIT);
	//int totnbin = MEASMODE_OWLT(J_cfg->MeasMode) ? TOT_NBIT : TOA_NBIT;
	//if (J_cfg->ToTHistoNbin > (1 << totnbin))	J_cfg->ToTHistoNbin = (1 << totnbin);
	// DNIN: Would histMax change in case of Streaming?
	// In CommonS Rebin=LSB, in Streaming there are two of them

	// Values used to trigger the CreateStatistics...Is it not enough to use the values those variables depends on?
	J_cfg->LeadHistoMax = (float)(J_cfg->LeadHistoMin + J_cfg->LeadTrail_LSB_ns * J_cfg->LeadTrailRebin * J_cfg->LeadTrailHistoNbin);
	J_cfg->TrailHistoMax = (float)(J_cfg->TrailHistoMin + J_cfg->LeadTrail_LSB_ns * J_cfg->LeadTrailRebin * J_cfg->LeadTrailHistoNbin);
	J_cfg->ToTHistoMax = (float)(J_cfg->ToTHistoMin + J_cfg->ToT_LSB_ns / 1000 * J_cfg->ToTRebin * J_cfg->ToTHistoNbin);

	// If the board is already connected, retrieve the number of channels from the BoardInfo
	// CTIN: can be different from board to board... For the moment, take NumCh from board 0 and extend it to all boards
	if (handle[0] != -1) {
		J_cfg->NumCh = FERS_NumChannels(handle[0]);
	}

	// Set Reading LL if offline is selected
	for (i = 0; i < J_cfg->NumBrd; ++i) {
		if (strstr(J_cfg->ConnPath[i], "offline") != NULL) {
			if (J_cfg->StopRunMode != STOPRUN_MANUAL) {
				Con_printf("LCSw", "WARNING: Manual Stop Run must be set when Raw Data are processed\n");
				J_cfg->StopRunMode = STOPRUN_MANUAL;
				Con_printf("SM", "StopRunMode:%d", STOPRUN_MANUAL);
			}
			if (J_cfg->EnableJobs != 0) {
				Con_printf("LCSw", "WARNING: Jobs are not permitted when Raw Data are processed\n");
				J_cfg->EnableJobs = 0;
				Con_printf("SM", "EnableJobs:%d", J_cfg->EnableJobs);
			}
			break;
		}
	}

	// WARNING that with A5256 the Tref must be set on "ch0"
	uint32_t Tref_Mask = 0;
	Tref_Mask = FERS_GetParam_uint32(handle[0], "Tref_Mask");
	if ((J_cfg->AdapterType == ADAPTER_A5256 || J_cfg->AdapterType == ADAPTER_A5256_REV0_NEG || J_cfg->AdapterType == ADAPTER_A5256_REV0_POS) &&
		Tref_Mask != 0) {
		Con_printf("LCSw", "WARNING: A5256 adapter requires Tref set as ch0. Setting Tref Source as CH0\n");
		Tref_Mask = 0;
			Con_printf("SM", "TrefSource:%d", Tref_Mask);
	}

#ifdef linux
	if (J_cfg->DataFilePath[strlen(J_cfg->DataFilePath) - 1] != '/')	sprintf(J_cfg->DataFilePath, "%s/", J_cfg->DataFilePath);
#else
	if (J_cfg->DataFilePath[strlen(J_cfg->DataFilePath) - 1] != '\\')	sprintf(J_cfg->DataFilePath, "%s\\", J_cfg->DataFilePath);
#endif

	// SetParam for RawData saving
	char buffLS[50];
	char buffMS[50];
	sprintf(buffLS, "%" PRIu8, J_cfg->EnableMaxFileSize);
	sprintf(buffMS, "%f", J_cfg->MaxOutFileSize);
	for (int i = 0; i < J_cfg->NumBrd; ++i) {
		FERS_SetParam(handle[i], "OF_RawDataPath", J_cfg->DataFilePath);
		FERS_SetParam(handle[i], "OF_LimitedSize", buffLS);
		FERS_SetParam(handle[i], "MaxSizeOutputDataFile", buffMS);
	}
	return 0;
}



// isForJanus:
//	0 FERSlib + Janus configuration
//  1 Janus configuration only
//int ParseConfigFile_OnGoing(FILE* f_ini, Config_t* J_cfg, bool fcall, int isForJanus)
//{
//	if (isForJanus) {
//		isJanusCfg = 1;
//		ParseConfigFile(f_ini, J_cfg, fcall);
//	} else {
//		isJanusCfg = 1;
//		ParseConfigFile(f_ini, J_cfg, fcall);
//		isJanusCfg = 0;
//		ParseConfigFile(f_ini, J_cfg, fcall);
//	}
//	return 0;
//}