/*
 * SCPI_parser.c
 *
 * Created: 10.3.2016 21:02:45
 *  Author: Chenda
 */ 
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>

#include "../../../Common/DataOperations.h"
#include "../../../Command_defs.h"
#include "SCPI_defs.h"
#include "SCPI_parser.h"
#include "SCPI_param_parser.h"

//#include <avr/io.h>
//#include "../../lcd/lcd_h.h"

/************************************************************************/
/* global variables                                                     */
/************************************************************************/
/* structures etc for parsing message */
static char ScpiTempMessBuff[MAX_MESS_LENTH];
static const struct stScpi_command *cspScpiCmdTemplates;
static struct stScpi_messageIN stMessageIn;
static struct stScpi_messageIN *spMessIn = &stMessageIn;
static struct stScpi_messageTEMP stMessageTemplate;
static struct stScpi_messageTEMP *spMessTemplate = &stMessageTemplate; 

/* structure for sharing data with higher layer */
static struct stScpi_ParsingStatus stParsingStatus;
static struct stScpi_ParsingStatus *spParStat = &stParsingStatus;

/************************************************************************/
/* private functions definition                                         */
/************************************************************************/
// preparing messages for comparison
void ParseScpiMess(void);
void SplitInMess_IntoNode(void);
void CopyTempMessToBuff(uint8_t MessIndex);
void SplitTempMess_IntoNode(void);
// comparison messages message in vs messages from template list
void CompareMessages (void);
uint8_t CompareNodes (struct stMessNode *NodeIn, struct stMessNode *NodeTemp, bool TempMandatory);
uint8_t RegularNodeComp (struct stMessNode *NodeIn, struct stMessNode *NodeTemp);
uint8_t GetNodeLength(char *NodeCahar, uint8_t NodeLen, uint8_t mode);
uint8_t MakeNode(char *NodeCahar, uint8_t NodeLen, char *NewNodeCahar, uint8_t mode);
bool NodeCompare(char *Node1, char *Node2, uint8_t Len);
// clearing structures prior parsing
void ClearPriorParsing(void);
void ClearParsingStatus(void);
void ClearMessIn(void);
void ClearMessTemp(void);

/************************************************************************/
/* global functions                                                     */
/************************************************************************/
struct stScpi_ParsingStatus* SCPI_PARSER_INIT()
{
	cspScpiCmdTemplates = GET_SCPI_PRGLAN_LIST();
	return spParStat;
}

uint8_t SCPI_PARSER_TASK(char *SCPI_messIn, uint8_t SCPI_messLength)
{
	ClearPriorParsing();
	spMessIn->OriginalMess = SCPI_messIn;
	spMessIn->OriginalMessLen = SCPI_messLength;	
	if (spMessIn->OriginalMess != NULL && spMessIn->OriginalMessLen != 0) {
		ParseScpiMess();
		if (spParStat->ParsingResult == SCPI_PARSING_PASS) {
			spParStat->ParsedCmd = cspScpiCmdTemplates[spMessTemplate->TempMessIndex].ProgLangCmd;
			spParStat->Query = spMessIn->Query;
			spParStat->UnparsedParam = spMessIn->Param;
			spParStat->UnparsedParLen = spMessIn->Param_len;
			SCPI_PARSE_PARAM(spParStat);
		}
	}
	return spParStat->ParsedCmd;
}

uint8_t SCPI_NUM_TO_ASCII(int16_t IntegerNo, int16_t TenthNo, char *SCPI_messOut)
{
	char buff[] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0};
	char *ptr = buff;
	uint8_t index;
	int16_t I = IntegerNo;
	int16_t T = TenthNo;
	bool TenthPresent;

	if (I < 0 || T < 0) {
		*ptr++ = '-';
		I = abs(I);
		T = abs(T);
	}	
	if (T == 0) {
		sprintf(ptr,"%d",I);
		TenthPresent = false;
		index = 0;
		while(buff[index] != 0) index++;
		index--;
	}
	else {
		sprintf(ptr,"%d.%04d",I,T);
		TenthPresent = true;
		index = sizeof(buff) - 1;
	}
	while((buff[index] == 0 || buff[index] == '0') && index > 0 && TenthPresent) {
		buff[index--] = 0;	
	}
	
	index++;
	DAT_OPS_MoveData((uint8_t*)buff,(uint8_t*)SCPI_messOut,index,false);
	return index;
}

/************************************************************************/
/* Private functions                                                    */
/************************************************************************/
void ParseScpiMess(void)
{
	uint8_t i=0;
	SplitInMess_IntoNode();
	if (spParStat->ParsingResult == SCPI_PARSING_NORESULT) {
		// message was split successfully
		while (cspScpiCmdTemplates[i].ProgLangCmd ){
			ClearMessTemp();
			CopyTempMessToBuff(i);
			SplitTempMess_IntoNode();
			CompareMessages();
			i++;
		}
		if (spParStat->ParsingResult == SCPI_PARSING_NORESULT) {
			// parsing unsuccessful
			spParStat->ParsingResult = SCPI_PARSING_CMD_FAIL;
		}
		if (spParStat->ParsingResult == SCPI_PARSING_PASS) {
			// parsing pass
			spParStat->ParsedCmd = cspScpiCmdTemplates[spMessTemplate->TempMessIndex].ProgLangCmd;
			spParStat->Query = spMessIn->Query;
			spParStat->UnparsedParam = spMessIn->Param;
			spParStat->UnparsedParLen = spMessIn->Param_len;
		}
	}
}

/************************************************************************/
/* comparison messages message in vs messages from template list        */
/************************************************************************/
void CompareMessages(void)
{
	uint8_t MI_NI = 0;		// Message in node index
	uint8_t MT_NI = 0;		// Message temp node index
	uint8_t CompStatus;		// comparison status - driving next action
	bool AttemptContinue = true;	// true if comparison should continue in next iteration
	if (spParStat->ParsingResult == SCPI_PARSING_NORESULT) {
		while(MI_NI < MAX_NODE_LEVELS && MT_NI < MAX_NODE_LEVELS && AttemptContinue){
			CompStatus = CompareNodes(&(spMessIn->Node[MI_NI]),&(spMessTemplate->Node[MT_NI]),spMessTemplate->MandatoryNode[MT_NI]);
			switch(CompStatus) {
				case 0:		// comparison fail on mandatory node
					AttemptContinue = false;
					break;
				case 1:		// comparison fail on optional node
					MT_NI++;
					break;
				case 2:		// comparison pass on mandatory node
					MT_NI++;
					MI_NI++;
					break;
				case 3:		// comparison pass on optional node
					MT_NI++;
					MI_NI++;
					break;
				case 4:		// comparison on blank nodes - no more node comparison is needed
					MI_NI = MAX_NODE_LEVELS;
					break;
				case 5:		// comparison fail, mess in is longer than temp mess
					AttemptContinue = false;
					break;
				case 6:		// comparison fail, bad long naming
					AttemptContinue = false;
					break;
				default:	// unknown status - fail happened
					AttemptContinue = false;
					break;
			}
		}
	}
	if (AttemptContinue && spParStat->ParsingResult == SCPI_PARSING_NORESULT) {
		// node comparison end successful
		//lcd_putc('1');
		if (spMessIn->Query == spMessTemplate->Query) {
			// query comparison successful
			spParStat->ParsingResult = SCPI_PARSING_PASS;
			//lcd_putc('2');
		}
	} 
	else {
		//lcd_putc('3');
	}
}

uint8_t CompareNodes(struct stMessNode *NodeIn, struct stMessNode *NodeTemp, bool TempMandatory)
{
	// 0: comparison fail on mandatory node - different node
	// 1: comparison fail on optional node - different node
	// 2: comparison pass on mandatory node
	// 3: comparison pass on optional node
	// 4: comparison on blank nodes - no more node comparison is needed
	// 5: comparison fail, mess in is longer than temp mess
	// 6: comparison fail, bad long naming
	uint8_t ComparisonStat = 0;
	uint8_t RegularComparisonRes = 0;
	if (spParStat->ParsingResult == SCPI_PARSING_NORESULT) {
		if(NodeIn->strLen > 0 && NodeTemp->strLen > 0) {
			// regular comparison
			RegularComparisonRes = RegularNodeComp(NodeIn,NodeTemp);
			switch(RegularComparisonRes) {
				case 0:		// comparison fail on node completely different - different short naming
					if (TempMandatory) {
						ComparisonStat = 0;
					}
					else {
						ComparisonStat = 1;
					}
					break;
				case 1:		// comparison fail on long naming - different long naming
					ComparisonStat = 6;
					break;
				case 2:		// comparison pass on short naming
					if (TempMandatory) {
						ComparisonStat = 2;	
					} 
					else {
						ComparisonStat = 3;
					}
					break;		
				case 3:		// comparison pass on long naming
					if (TempMandatory) {
						ComparisonStat = 2;
					}
					else {
						ComparisonStat = 3;
					}
					break;
				default:
					ComparisonStat = 255;
					break; 
			}
			
		}
		else if (NodeIn->strLen > 0 && NodeTemp->strLen == 0) {
			// comparison fail - mess in is longer than temp mess
			ComparisonStat = 5;
		}
		else if (NodeIn->strLen == 0 && NodeTemp->strLen > 0) {
			// Mess in node is empty but if temp node is optional it is OK
			if (TempMandatory) {
				ComparisonStat = 0;
			}
			else {
				ComparisonStat = 1;
			}
		}
		else {
			// all nodes are zero
			ComparisonStat = 4;
		}
	}	
	return ComparisonStat;
}

uint8_t RegularNodeComp(struct stMessNode *NodeIn, struct stMessNode *NodeTemp)
{
	// return val:
	// 0: comparison fail on node completely different - different short naming
	// 1: comparison fail on long naming - different long naming
	// 2: comparison pass on short naming
	// 3: comparison pass on long naming
	//steps:
	// 1)  get short & long len of temp node - all upper case letters without numbers
	// 2)  get length of node in - without numbers
	// 3a) if length of node in = short temp node -> make temp short node
	// 3b) if length of node in > short temp node -> make temp long node
	// 4)  compare nodes
	// 4a) if short part isn't same -> node completely different
	// 4b) if short part is the same -> OK & and check rest if comparing long node
	
	uint8_t ComparisonStat = 0;
	bool NodesMatch = true;
	char TempCharBuff[MAX_MNEMONIC_LENGTH];
	char *pTempChar = TempCharBuff;
	uint8_t TempShortLen = 0;
	uint8_t TempFinalLen = 0;
	char *pInChar = NodeIn->pChar;
	uint8_t InLength = 0;
	uint8_t InFinalLen = NodeIn->strLen;
	
	if (spParStat->ParsingResult == SCPI_PARSING_NORESULT) {
		TempShortLen = GetNodeLength(NodeTemp->pChar,NodeTemp->strLen,1);
		InLength = GetNodeLength(NodeIn->pChar,NodeIn->strLen,0);
		if (InLength <= TempShortLen) {
			// create short node	
			TempFinalLen = MakeNode(NodeTemp->pChar,NodeTemp->strLen,pTempChar,1);		
		}
		else {
			// create long node
			TempFinalLen = MakeNode(NodeTemp->pChar,NodeTemp->strLen,pTempChar,0);
		}
		if (NodesMatch && InLength < TempShortLen) {
			// NodeIn doesn't match even short temp node - completely different
			NodesMatch = false;
			ComparisonStat = 0;
		}
		if (NodesMatch && InLength >= TempShortLen) {
			// check for matching short format
			if (!NodeCompare(pInChar,pTempChar,TempShortLen)) {
				// comparison fail on short format - different command
				NodesMatch = false;
				ComparisonStat = 0;
			}
		}
		if (NodesMatch && InLength == TempShortLen && InFinalLen == TempFinalLen) {
			// in node is in short format
			if (NodeCompare(pInChar,pTempChar,TempFinalLen)) {
				NodesMatch = false;
				ComparisonStat = 2;
			}
			else {
				ComparisonStat = 0;
			}
		}
		if (NodesMatch && InFinalLen == TempFinalLen) {
			// in node is in long format
			if (NodeCompare(pInChar,pTempChar,TempFinalLen)) {
				NodesMatch = false;
				ComparisonStat = 3;
			}
			else {
				ComparisonStat = 1;
			}
		}
		if (NodesMatch) {
			ComparisonStat = 1;
		}
	}
	return ComparisonStat;
}

uint8_t GetNodeLength(char *NodeCahar, uint8_t NodeLen, uint8_t mode)
{
	uint8_t length = 0;
	uint8_t index = 0;
	while(index < NodeLen) {
		switch(mode) {
			case 0:		// all alphabet
				if (isalpha(*NodeCahar)) length++;
				break;
			case 1:		// only upper case letters - short
				if (isupper(*NodeCahar)) length++;
				break;
			default:	// all alphabet
				if (isalpha(*NodeCahar)) length++;
				break;
		}
		NodeCahar++;
		index++;
	}
	return length;
}

uint8_t MakeNode(char *NodeCahar, uint8_t NodeLen, char *NewNodeCahar, uint8_t mode)
{
	uint8_t NewLength = 0;
	uint8_t index = 0;
	while (index < NodeLen) {
		switch(mode) {
			case 0:		// no change, just copy
				*NewNodeCahar++ = *NodeCahar;
				NewLength++;
				break;
			case 1:		// copy upper case letters and numbers only
				if (isupper(*NodeCahar) || isdigit(*NodeCahar) || *NodeCahar=='*') {
					*NewNodeCahar++ = *NodeCahar;
					NewLength++;
				}
				break;
			default:	// no change, just copy
				*NewNodeCahar++ = *NodeCahar;
				NewLength++;
				break;
		}
		NodeCahar++;
		index++;
	}
	*NewNodeCahar = 0;
	return NewLength;
}

bool NodeCompare(char *Node1, char *Node2, uint8_t Len)
{
	bool NodeMatch = true;
	while(Len--) {
		if (toupper(*Node1++) != toupper(*Node2++)) NodeMatch = false;
	}
	return NodeMatch;
}

/************************************************************************/
/* Splitting messages into nodes                                        */
/************************************************************************/
void SplitInMess_IntoNode(void)
{
	// incoming message split into nodes
	uint8_t NodeLen = 0;
	uint8_t NodeLevel = 0;
	struct stMessNode *pNode = spMessIn->Node;
	char *Mess = spMessIn->OriginalMess;
	
	if (spParStat->ParsingResult == SCPI_PARSING_NORESULT) {
		// parsing in process - OK
		if (*Mess == ':') {
			// skipping first colon if exist
			Mess++;
		}
		pNode->pChar = Mess;
		while( (isalnum(*Mess) || *Mess == ':' || *Mess == '?'|| *Mess == '*')  && NodeLevel < MAX_NODE_LEVELS) {
			if (isalnum(*Mess) || *Mess == '?' || *Mess == '*') {
				NodeLen++;
			}
			if (*Mess == ':') {
				// store length last node
				pNode->strLen = NodeLen;
				//Starting next node
				NodeLen = 0;
				NodeLevel++;
				pNode++;
				pNode->pChar = Mess+1;
			}
			Mess++;
		}
		if (*Mess == ' ' || *Mess == 0 || *Mess == '\n' || *Mess == '\r') {
			// correct end of command part
			pNode->strLen = NodeLen;
			// query detection on last node
			if (*(pNode->pChar + pNode->strLen-1)=='?') {
				spMessIn->Query = true;
				pNode->strLen--;
			}
			// storing param
			spMessIn->Param_len = 0;
			spMessIn->Param = Mess;
			if (*Mess == ' ') {
				NodeLen = 0;
				Mess++;
				spMessIn->Param = Mess;
				//while(isgraph((int)*Mess++)) {
				while(isgraph((int)*Mess) || *Mess == ' ') {
					Mess++;
					NodeLen++;
				}
				spMessIn->Param_len = NodeLen;
			}
			if (spMessIn->Param_len == 0) {
				// clearing position with zero param length
				*spMessIn->Param = 0;
			}
						
		}
		else {
			// splitting error
			spParStat->ParsingResult = SCPI_PARSING_CMD_FAIL;
		}
	}
}

void CopyTempMessToBuff(uint8_t MessIndex)
{
	const char *cpSrc = cspScpiCmdTemplates[MessIndex].SCPI_mess;
	char *pTarg = ScpiTempMessBuff;
	if (spParStat->ParsingResult == SCPI_PARSING_NORESULT) {
		spMessTemplate->TempMessIndex = MessIndex;
		while(*cpSrc) {
			*pTarg++ = *cpSrc++;
		}
		*pTarg = 0;
	}
}

void SplitTempMess_IntoNode(void)
{
	uint8_t NodeLen = 0;
	uint8_t NodeLevel = 0;
	struct stMessNode *pNode = spMessTemplate->Node;
	char *Mess = ScpiTempMessBuff;
	bool *ManNode = spMessTemplate->MandatoryNode;
	if (spParStat->ParsingResult == SCPI_PARSING_NORESULT) {
		*ManNode = true;
		if (*Mess == '[') {
			// first node(root node) isn't mandatory
			*ManNode = false;
			Mess++;
		}
		pNode->pChar = Mess;
		while( (isalnum(*Mess) || *Mess == ':' || *Mess == '?'|| *Mess == '*' || *Mess == '[' || *Mess == ']')  
				&& NodeLevel < MAX_NODE_LEVELS ) {
			if (isalnum(*Mess) || *Mess == '?'|| *Mess == '*' ) {
				NodeLen++;
			}
			if (*Mess == ':') {
				// store length last node
				pNode->strLen = NodeLen;
				//Starting next node
				NodeLen = 0;
				NodeLevel++;
				pNode++;
				pNode->pChar = Mess+1;
				ManNode++;
				*ManNode = true;
				if (*(Mess-1) == '[') {
					*ManNode = false;
				}
				if (*(pNode->pChar) == ']') {
					pNode->pChar++;
				}
			}
			Mess++;
		}
		// correct end of command part
		pNode->strLen = NodeLen;	
		// query detection on last node
		if (*(pNode->pChar + pNode->strLen-1)=='?') {
			spMessTemplate->Query = true;
			pNode->strLen--;
		}
		else {
			spMessTemplate->Query = false;
		}
	}
}

/************************************************************************/
/* Clearing prior parsing functions                                     */
/************************************************************************/
void ClearPriorParsing(void)
{
	ClearParsingStatus();
	ClearMessIn();
	ClearMessTemp();
}

void ClearParsingStatus(void)
{
	spParStat->ParsedCmd = NO_COMMAND;
	spParStat->ParsingResult = SCPI_PARSING_NORESULT;
	spParStat->Query = false;
	spParStat->UnparsedParam = NULL;
	spParStat->UnparsedParLen = 0;
	spParStat->IntPartOfNumber = 0;
	spParStat->TenthPartOfNumber = 0;
	spParStat->ValueOfNumber = 0;
}

void ClearMessIn(void)
{
	struct stMessNode *pNode = spMessIn->Node;
	spMessIn->OriginalMess = NULL;
	spMessIn->OriginalMessLen = 0;
	spMessIn->Query = false;
	spMessIn->Param = NULL;
	spMessIn->Param_len = 0;
	for(uint8_t i=0;i<MAX_NODE_LEVELS;i++) {
		pNode->pChar = NULL;
		pNode->strLen = 0;
		pNode++;
	}
}

void ClearMessTemp(void)
{
	struct stMessNode *pNode = spMessTemplate->Node;
	bool *ManNode = spMessTemplate->MandatoryNode;
	if (spParStat->ParsingResult == SCPI_PARSING_NORESULT) {
		spMessTemplate->Query = false;
		spMessTemplate->ScpiTempMess = NULL;
		spMessTemplate->ScpiTempMessLen = 0;
		spMessTemplate->TempMessIndex = -1;
		for(uint8_t i=0;i<MAX_NODE_LEVELS;i++) {
			pNode->pChar = NULL;
			pNode->strLen = 0;
			*ManNode = false;
			ManNode++;
			pNode++;
		}
	}
}