// ==========================================================================
// config.c
// (c) 2020, Aurel Dumitru
//
// Description:
// Configuration management
// =========================================================================

#include <string.h>
#include "config.h"
#include "file.h"
#include "control.h"
#include "rtc.h"
#include "sensor_nfc.h"
#include "devstatus.h"
#include "calibrations.h"

struct Config_TextStruct
{	char* Start;
	char* End;
};

struct Config_AttributeOpStruct
{	char Attribute[32] ;
	void (*OpAttributeValue)(struct Config_TextStruct Text);
};

void Config_TypeOp(struct Config_TextStruct Text);
void Config_DeviceTypeOp(struct Config_TextStruct Text);
void Config_CommunicationRecurrenceOp(struct Config_TextStruct Text);
void Config_RgbLedTimeAndColorOp(struct Config_TextStruct Text);
void Config_PheromoneAirCirculationOp(struct Config_TextStruct Text);
void Config_ActiveInsectsOp(struct Config_TextStruct Text);
void Config_TimeCorrectionOp(struct Config_TextStruct Text);
void Config_PhoneNumberSmsOp(struct Config_TextStruct Text);
void Config_FormatMicroSdOp(struct Config_TextStruct Text);
void Config_FreezeAlertOp(struct Config_TextStruct Text);
void Config_FreezeThresholdOp(struct Config_TextStruct Text);

char ConfigFile[8*1024];
char Config_CommRecStatString[8] = "CR=1";
char Config_PherAirStatString[12] = "PH=N/A";
char Config_RgbStatString[20] = "RGB=N/A";
char Config_ActInsectsStatString[128] = "AI=N/A";
char Config_FreezeTempStatString[12] = "FT=N/A";
char Config_AllStatString[256];

struct Config_TimeEventStruct Config_TimeEventsTable[24] =
{
// Hour		// Modem Request	// Temperature/Humid Request	Pheromone Flap Request	// RGB LED (<=0xFFFFFF is color, >0xFFFFFF do nothing)
/*00*/		{1,					1,								0,						0xFFFFFFFF},
/*01*/		{1,					1,								0,						0xFFFFFFFF},
/*02*/		{1,					1,								0,						0xFFFFFFFF},
/*03*/		{1,					1,								0,						0xFFFFFFFF},
/*04*/		{1,					1,								0,						0xFFFFFFFF},
/*05*/		{1,					1,								0,						0xFFFFFFFF},
/*06*/		{1,					1,								0,						0xFFFFFFFF},
/*07*/		{1,					1,								0,						0xFFFFFFFF},
/*08*/		{1,					1,								0,						0xFFFFFFFF},
/*09*/		{1,					1,								0,						0xFFFFFFFF},
/*10*/		{1,					1,								0,						0xFFFFFFFF},
/*11*/		{1,					1,								0,						0xFFFFFFFF},
/*12*/		{1,					1,								0,						0xFFFFFFFF},
/*13*/		{1,					1,								0,						0xFFFFFFFF},
/*14*/		{1,					1,								0,						0xFFFFFFFF},
/*15*/		{1,					1,								0,						0xFFFFFFFF},
/*16*/		{1,					1,								0,						0xFFFFFFFF},
/*17*/		{1,					1,								0,						0xFFFFFFFF},
/*18*/		{1,					1,								0,						0xFFFFFFFF},
/*19*/		{1,					1,								0,						0xFFFFFFFF},
/*20*/		{1,					1,								0,						0xFFFFFFFF},
/*21*/		{1,					1,								0,						0xFFFFFFFF},
/*22*/		{1,					1,								0,						0xFFFFFFFF},
/*23*/		{1,					1,								0,						0xFFFFFFFF},
};

uint8_t	 Config_FreezeAlert = 0;
int16_t  Config_FreezeThreshold = 0;
uint8_t  Config_NumberOfActiveInsects = 0;
uint16_t Config_ActiveInsectsIdx[32];
uint32_t Config_TimeCorrection;
char	 Config_PhoneNumberSms[24];



const struct Config_AttributeOpStruct Config_AttributeOp[] =
{
	{"Type",						Config_TypeOp},
	{"DeviceType",					Config_DeviceTypeOp},
	{"TimeCorrection",				Config_TimeCorrectionOp},
	{"CommunicationRecurrence",		Config_CommunicationRecurrenceOp},
	{"PheromoneAirCirculation",		Config_PheromoneAirCirculationOp},
	{"RgbLedTimeAndColor",			Config_RgbLedTimeAndColorOp},
	{"ActiveInsects",				Config_ActiveInsectsOp},
	{"PhoneNumberSms",				Config_PhoneNumberSmsOp},
	{"FreezeAlert",					Config_FreezeAlertOp},

	// Imediate actions
	{"A_FormatMicroSd",				Config_FormatMicroSdOp},
	{"A_FreezeThreshold",			Config_FreezeThresholdOp},
};

const uint32_t Config_NumberOfAttribute = sizeof(Config_AttributeOp)/sizeof(Config_AttributeOp[0]);

int32_t Config_SearchAttribute(struct Config_TextStruct Text)
{
	const char* SrcA;
	const char* SrcB;

	for (uint32_t I=0; I<Config_NumberOfAttribute; I++)
	{
		SrcA = Text.Start;
		SrcB = Config_AttributeOp[I].Attribute;
		while ((*SrcB != '\0') && (SrcA != Text.End))
			if (*SrcA++ != *SrcB++) break;
		if ((*SrcB == '\0') && (SrcA == Text.End))	return I;
	}
	return -1;
}

struct Config_TextStruct Config_TextTrim(struct Config_TextStruct Text)
{
	char* Src = Text.Start;
	while (1)
		if (((*Src >= 'A') && (*Src <= 'Z')) ||
			((*Src >= 'a') && (*Src <= 'z')) ||
			((*Src >= '0') && (*Src <= '9')) ||
			(*Src == '+') || (*Src == '-') || (*Src == '.')) break;
		else Src++;
	Text.Start = Src;
	Src = Text.End - 1;
	while (1)
		if (((*Src >= 'A') && (*Src <= 'Z')) ||
			((*Src >= 'a') && (*Src <= 'z')) ||
			((*Src >= '0') && (*Src <= '9')) ||
			(*Src == '+') || (*Src == '-') || (*Src == '.')) break;
		else Src--;
	Text.End = Src + 1;


	return Text;
}

char* Config_CharPos(struct Config_TextStruct Text, char Char)
{
	char* End = NULL;
	char* Ptr = Text.Start;
	while (Ptr != Text.End)
	{
		if (*Ptr == Char) {End = Ptr; break;}
		Ptr++;
	}
	return End;
}

void Config_ParseConfigFile(void)
{
	struct Config_TextStruct Text, Attribute;
	char* ConfigTxt = ConfigFile;
	char* Ptr;
	int32_t I;
	uint32_t Length = File_ReadFileNwm("cnf.txt", ConfigTxt);

	Config_PhoneNumberSms[0] = '\0';
	ConfigTxt[Length] = '\0';
	while (1)
	{
		while ((*ConfigTxt == '\n') || (*ConfigTxt == '\r') || (*ConfigTxt == ' ')) ConfigTxt++;
		if (*ConfigTxt == '\0') break;
		Text.Start = ConfigTxt;
		while ((*ConfigTxt != '\n') && (*ConfigTxt != '\r') && (*ConfigTxt != '\0')) ConfigTxt++;
		Text.End = ConfigTxt;
		if ((Ptr = Config_CharPos(Text, '=')))
		{
			Attribute.Start = Text.Start;
			Attribute.End	= Ptr;
			I = Config_SearchAttribute(Config_TextTrim(Attribute));
			if (I >= 0)
			{
				Attribute.Start = Ptr+1;
				Attribute.End	= Text.End;
				Config_AttributeOp[I].OpAttributeValue(Config_TextTrim(Attribute));
			}
		}
	}
	strcpy(Config_AllStatString, Config_CommRecStatString); 	strcat(Config_AllStatString, ",");
	strcat(Config_AllStatString, Config_PherAirStatString); 	strcat(Config_AllStatString, ",");
	strcat(Config_AllStatString, Config_RgbStatString);			strcat(Config_AllStatString, ",");
	strcat(Config_AllStatString, Config_FreezeTempStatString);	strcat(Config_AllStatString, ",");
	strcat(Config_AllStatString, Config_ActInsectsStatString);
}


uint32_t Config_ParseConfigFileImmediateActions(char* ConfigTxt)
{
	struct Config_TextStruct Text, Attribute;
	char* Ptr;
	int32_t I;
	uint32_t ActionsPresent = 0;

	while (1)
	{
		while ((*ConfigTxt == '\n') || (*ConfigTxt == '\r') || (*ConfigTxt == ' ')) ConfigTxt++;
		if (*ConfigTxt == '\0') break;
		Text.Start = ConfigTxt;
		while ((*ConfigTxt != '\n') && (*ConfigTxt != '\r') && (*ConfigTxt != '\0')) ConfigTxt++;
		Text.End = ConfigTxt;
		if ((Ptr = Config_CharPos(Text, '=')))
		{
			Attribute.Start = Text.Start;
			Attribute.End	= Ptr;
			Attribute = Config_TextTrim(Attribute);
			if ((*(Attribute.Start) == 'A') && (*(Attribute.Start+1) == '_'))
			{
				ActionsPresent = 1;
				I = Config_SearchAttribute(Attribute);
				if (I >= 0)
				{
					Attribute.Start = Ptr+1;
					Attribute.End	= Text.End;
					Config_AttributeOp[I].OpAttributeValue(Config_TextTrim(Attribute));
				}
			}
		}
	}
	return ActionsPresent;
}


int32_t Config_GetInteger(struct Config_TextStruct Text)
{
	int32_t Value = 0;
	int32_t Sign = 1;
	if ((*Text.Start == '-') || (*Text.Start == '+'))	Sign = ',' - (*Text.Start++) ;
	uint32_t Length = Text.End - Text.Start;
	for (; Length>0; Length--)
			if ((*Text.Start >= '0') && (*Text.Start <= '9'))		Value = (Value*10) + ((*Text.Start++)-'0');
			else 													return NC_INVALID_INTEGER;
	return (Sign*Value);
}

uint32_t Config_GetHex(struct Config_TextStruct Text)
{
	uint32_t Value = 0;
	uint32_t Length = Text.End - Text.Start;
	for (; Length>0; Length--)
			if ((*Text.Start >= '0') && (*Text.Start <= '9'))		Value = (Value*16) + ((*Text.Start++)-'0');
			else if ((*Text.Start >= 'A') && (*Text.Start <= 'F'))	Value = (Value*16) + ((*Text.Start++)-'A')+10;
				 else 												return NC_INVALID_INTEGER;
	return Value;
}



void Config_TypeOp(struct Config_TextStruct Text)
{
	// it shall be coded for NEW / UPDATE
}

void Config_DeviceTypeOp(struct Config_TextStruct Text)
{
	if (*Text.Start == 'S')
		DevStatus_SetDeviceType(1);		//force device type "Sensors"
}

void Config_FormatMicroSdOp(struct Config_TextStruct Text)
{
	File_FormatMicroSd();
}

void Config_FreezeAlertOp(struct Config_TextStruct Text)
{
	char Tmp[4];

	Config_FreezeAlert = (uint8_t)Config_GetInteger(Text);
	if (Config_FreezeAlert)
	{
		Sensor_Nfc_PowerUp();
		Sensor_Nfc_ReadProtectedData(SENSOR_NFC_PROTDATA_FREEZETEMP_ADDR, 2, (uint8_t*)(&Config_FreezeThreshold));
		if ((Config_FreezeThreshold < -400) || (Config_FreezeThreshold > 600))
		{
			Config_FreezeThreshold = -400;
			Sensor_Nfc_WriteProtectedData(SENSOR_NFC_PROTDATA_FREEZETEMP_ADDR, 2, (uint8_t*)(&Config_FreezeThreshold));
		}
		Sensor_Nfc_PowerDown();
		char *Dest = Config_FreezeTempStatString+3;
		int16_t FreezeThreshold = Config_FreezeThreshold;
		if (FreezeThreshold < 0)
		{
			FreezeThreshold = -FreezeThreshold;
			*Dest++ = '-';
		}
		strcpy(Dest, itoa(FreezeThreshold/10,Tmp,10));
		strcat(Dest, ".");
		strcat(Dest, itoa(FreezeThreshold%10,Tmp,10));
	}
}

void Config_FreezeThresholdOp(struct Config_TextStruct Text)
{
	int16_t FreezeThreshold = (int16_t)Config_GetInteger(Text);
	Sensor_Nfc_PowerUp();
	if (FreezeThreshold != NC_INVALID_INTEGER)
		Sensor_Nfc_WriteProtectedData(SENSOR_NFC_PROTDATA_FREEZETEMP_ADDR, 2, (uint8_t*)(&FreezeThreshold));
	Sensor_Nfc_PowerDown();
}

void Config_TimeCorrectionOp(struct Config_TextStruct Text)
{
	int32_t TimeShift = Config_GetInteger(Text);
	if (TimeShift != NC_INVALID_INTEGER)
	{
		Control_UpdateClockOnGps(TimeShift*60);
		File_WriteFileNwm("cnf.txt", (const char*)(Text.End), strlen(Text.End));
	}
}

void Config_PhoneNumberSmsOp(struct Config_TextStruct Text)
{
	char Tmp = *Text.End;
	*Text.End = '\0';
	strcpy(Config_PhoneNumberSms, Text.Start);
	*Text.End = Tmp;
}

void Config_PheromoneAirCirculationOp(struct Config_TextStruct Text)
{
	uint32_t StartHour, EndHour, I;
	struct Config_TextStruct Attribute;
	char Tmp[4];

	char* Ptr = Config_CharPos(Text, '-');
	if (Ptr)
	{
		Attribute.Start = Text.Start;
		Attribute.End	= Ptr;
		StartHour = Config_GetInteger(Config_TextTrim(Attribute));
		if ((StartHour != NC_INVALID_INTEGER) && (StartHour <= 24))
		{
			Attribute.Start = Ptr+1;
			Attribute.End	= Text.End;
			EndHour = Config_GetInteger(Config_TextTrim(Attribute));
			if ((EndHour != NC_INVALID_INTEGER) && (EndHour <= 24))
			{
				StartHour %= 24;
				EndHour   %= 24;
				// Check if required to disable
				if (StartHour == EndHour)
				{
					for (I=0; I<24; I++)
						Config_TimeEventsTable[I].PheromoneFlapReq = 0;
					strcpy(Config_PherAirStatString+3, "N/A");
				}
				else
				{
					for (I=StartHour; I != EndHour; I = (I+1)%24)
						if (I != NC_MOTOR_TEST_HOUR)
							Config_TimeEventsTable[I].PheromoneFlapReq = 1;
					for (; I != StartHour; I = (I+1)%24)
						Config_TimeEventsTable[I].PheromoneFlapReq = 0;
					strcpy(Config_PherAirStatString+3, itoa(StartHour,Tmp,10));
					strcat(Config_PherAirStatString, "-");
					strcat(Config_PherAirStatString, itoa(EndHour,Tmp,10));
				}
			}
		}
	}
}

void Config_CommunicationRecurrenceOp(struct Config_TextStruct Text)
{
	uint32_t StartHour, Recurrence, I;
	struct Config_TextStruct Attribute;
	char Tmp[4];

	char* Ptr = Config_CharPos(Text, ',');
	if (Ptr)
	{
		Attribute.Start = Text.Start;
		Attribute.End	= Ptr;
		StartHour = Config_GetInteger(Config_TextTrim(Attribute));
		if ((StartHour != NC_INVALID_INTEGER) && (StartHour <= 24))
		{
			Attribute.Start = Ptr+1;
			Attribute.End	= Text.End;
			Recurrence = Config_GetInteger(Config_TextTrim(Attribute));
			if ((Recurrence != NC_INVALID_INTEGER) && (Recurrence>0) && (Recurrence<=12))
			{
				uint32_t PerDay = 24/Recurrence;
				for (I=0; I<24; I++)
					Config_TimeEventsTable[I].ModemReq = 0;
				for (; PerDay>0; PerDay--)
				{	StartHour %= 24;
					Config_TimeEventsTable[StartHour].ModemReq = 1;
					StartHour += Recurrence;
				}
				strcpy(Config_CommRecStatString+3, itoa(Recurrence,Tmp,10));
			}
		}
	}
}

void Config_RgbLedTimeAndColorOp(struct Config_TextStruct Text)
{
	uint32_t StartHour, EndHour, Color, I;
	struct Config_TextStruct Attribute;
	struct Config_TextStruct ColorHexAttr;
	char Tmp[4];
	char* Ptr = Config_CharPos(Text, ',');

	if (Ptr)
	{
		Attribute.Start = Ptr+1;
		Attribute.End	= Text.End;
		ColorHexAttr	= Config_TextTrim(Attribute);
		Color = Config_GetHex(ColorHexAttr);
		if (Color <= 0xFFFFFF)
		{
			Text.End = Ptr;
			Ptr = Config_CharPos(Text, '-');
			if (Ptr)
			{
				Attribute.Start = Text.Start;
				Attribute.End	= Ptr;
				StartHour = Config_GetInteger(Config_TextTrim(Attribute));
				Attribute.Start = Ptr+1;
				Attribute.End	= Text.End;
				EndHour = Config_GetInteger(Config_TextTrim(Attribute));
				if ((StartHour>=0) && (StartHour<=24) && (EndHour>=0) && (EndHour<=24))
				{
					StartHour %= 24;
					EndHour   %= 24;
					if (StartHour == EndHour)
					{
						for (I=0; I<24; I++)
							Config_TimeEventsTable[I].RgbLedColor = 0xFFFFFFFF;
						strcpy(Config_RgbStatString+4, "N/A");
					}
					else
					{
						for (I=StartHour; I != EndHour; I = (I+1)%24)
							Config_TimeEventsTable[I].RgbLedColor = Color;
						if (Config_TimeEventsTable[EndHour].RgbLedColor > 0xFFFFFF)
							Config_TimeEventsTable[EndHour].RgbLedColor = 0x000000;
						memcpy(Config_RgbStatString+4, ColorHexAttr.Start, 6);
						Config_RgbStatString[10] = '/'; Config_RgbStatString[11] = '\0';
						strcat(Config_RgbStatString, itoa(StartHour,Tmp,10));
						strcat(Config_RgbStatString, "-");
						strcat(Config_RgbStatString, itoa(EndHour,Tmp,10));
					}
				}
			}
		}
	}
}


void Config_ActiveInsectsOp(struct Config_TextStruct Text)
{
	char* Ptr;
	const char* SrcA;
	const char* SrcB;
	struct Config_TextStruct Attribute;
	uint32_t I;

	do
	{
		Ptr = Config_CharPos(Text, ',');
		Attribute.Start = Text.Start;
		Attribute.End = (Ptr) ? Ptr:Text.End;
		Attribute = Config_TextTrim(Attribute);
		// Search insect name

		for (I=0; I<C_NumberOfInsectsModels; I++)
		{
			SrcA = Attribute.Start;
			SrcB = C_InsectsModels[I].Name;
			while ((*SrcB != '\0') && (SrcA != Attribute.End))
				if (*SrcA++ != *SrcB++) break;
			if ((*SrcB == '\0') && (SrcA == Attribute.End))
			{
				Config_ActiveInsectsIdx[Config_NumberOfActiveInsects++] = (uint16_t)I;
				break;
			}
		}
		Text.Start = Ptr+1;
	} while (Ptr);

	Config_ActInsectsStatString[3] = '\0';
	if (Config_NumberOfActiveInsects == 0)
		strcat(Config_ActInsectsStatString, "N/A");
	else
	{
		for (I=0; I<Config_NumberOfActiveInsects; I++)
		{
			strcat(Config_ActInsectsStatString, C_InsectsModels[Config_ActiveInsectsIdx[I]].ShortName);
			strcat(Config_ActInsectsStatString, "/");
		}
		Config_ActInsectsStatString[strlen(Config_ActInsectsStatString)-1] = '\0';
	}
}
