/*
 * SCPI_param_parser.c
 *
 * Created: 14.3.2016 19:24:55
 *  Author: Chenda
 */ 

#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>

#include "SCPI_defs.h"
#include "SCPI_param_parser.h"

/************************************************************************/
/* global variables                                                     */
/************************************************************************/
static struct stScpi_ParsingStatus *spParStat;

/************************************************************************/
/* private functions definition                                         */
/************************************************************************/
void TrimParam(void);

void ParseParam(void);
void ParseTextVal(void);
void ParseNumber(void);

/************************************************************************/
/* global functions                                                     */
/************************************************************************/
void SCPI_PARSE_PARAM(struct stScpi_ParsingStatus* spParsingStatus)
{
	spParStat = spParsingStatus;
	ParseParam();
}

/************************************************************************/
/* Private functions                                                    */
/************************************************************************/
void TrimParam(void)
{
	*(spParStat->UnparsedParam + spParStat->UnparsedParLen) = 0;
}

void ParseParam(void)
{
	if (spParStat->UnparsedParLen > 0) {
		// something to parse
		TrimParam();
		if (isalpha(*spParStat->UnparsedParam)) {
			// maybe text value
			ParseTextVal();
		} 
		else if(isdigit(*spParStat->UnparsedParam) || 
				*spParStat->UnparsedParam == '.' || 
				*spParStat->UnparsedParam == '-' ||
				*spParStat->UnparsedParam == '+') {
			// maybe number
			ParseNumber();
		}
		else {
			spParStat->ParsingResult = SCPI_PARSING_PAR_FAIL;
		}
	}
}

void ParseTextVal(void)
{
	uint8_t index = 0;
	bool textMatch = false;
	const struct stTextValus *spTextVal = GET_SCPI_TEXT_VAL_LIST();
	char *OriginChar;
	char *Param;

	while (*(OriginChar = (char *)spTextVal[index].Text) > 0 && textMatch == false) {
		// do while list not end or while match not found
		Param = spParStat->UnparsedParam;
		while(*OriginChar == toupper(*Param) && *Param > 0) {
			OriginChar++;
			Param++;
		}
		if (*OriginChar == 0 && (*Param == 0 || *Param == '\n' || *Param == '\r')) {
			// same chars, same length match
			textMatch = true;
		}
		else {
			index++;
		}
	}
	if (textMatch) {
		spParStat->IntPartOfNumber = spTextVal[index].RepreVal;
	}
	else {
		spParStat->ParsingResult = SCPI_PARSING_PAR_FAIL;
	}
}

void ParseNumber(void)
{
	const struct stParamUnit *spParUnit = GET_SCPI_UNIT_LIST();
	const struct stParamValue *spParValue = GET_SCPI_VALUE_LIST();
	float TempWholeNumber;
	int16_t TempInteger = 0;
	int16_t TempTenth = 0;
	int8_t multiplier = 0;
	uint8_t NumVal = 0;
	bool PrsStatus = true;
	uint8_t index;
	bool Minus = false;
	char buff[5] = {'0','0','0','0',0};
	char *pTempChar;
	char *pChar = spParStat->UnparsedParam;
		
	// check sign
	if (*pChar == '-') {
		Minus = true;
	}
	if (*pChar == '-' || *pChar == '+') {
		pChar++;
	}
	
	// catching integer
	index = 0;
	if (isdigit(*pChar) && PrsStatus) {
		while(isdigit(*pChar)) {
			index++;	
			pChar++;
		}
		// end of digit section
		pTempChar = pChar;
		if (index > 4) index = 4;		// preventing over flow
		for (;index>0;index--) pTempChar--;
		TempInteger = atoi(pTempChar);
	}
	
	// catching tenth
	index = 0;
	if (*pChar == '.' && PrsStatus) {
		// tenth stage exists
		pChar++;
		pTempChar = pChar;
		while(isdigit(*pChar) && index<4) {
			buff[index] = *pChar;
			index++;
			pChar++;
		}
		TempTenth = atoi(buff);
		while(isdigit(*pChar)) pChar++;		//rest of numbers
	}
	
	// max 2 chars should left
	if (isalpha(*pChar)) {
		index = 0;
		// catching unit
		while(spParUnit[index].unitSymbol) {
			if (spParUnit[index].unitSymbol == *pChar) {
				multiplier = spParUnit[index].multiplier;
			}
			index++;
		}
		if (multiplier != 0) {
			pChar++;
		}
		// catching value
		if (!(*pChar == 0 || *pChar == '\n' || *pChar == '\r')) {
			index = 0;
			while(spParValue[index].valueSymbol) {
				if (spParValue[index].valueSymbol == *pChar) {
					NumVal = spParValue[index].NoOfVal;
				}
				index++;
			}
			pChar++;
			if (!(NumVal != 0 && (*pChar == 0 || *pChar == '\n' || *pChar == '\r'))) {
				PrsStatus = false;
			}
		}
		else {
			// end of param
			if (multiplier == 0) {
				// only one char and that wasn't unit
				PrsStatus = false;
			}
		}
	}
	else if (!(*pChar == 0 || *pChar == '\n' || *pChar == '\r')) {
		// placed something else - fail
		PrsStatus = false;
	}
	
	// storing data 
	if (PrsStatus) {
		// build number
		TempWholeNumber = TempTenth;
		TempWholeNumber /= 10000;
		TempWholeNumber += TempInteger;
		
		switch(multiplier) {
			case UNIT_MULTI_MILI:
				TempWholeNumber /= 1000;
				break;
			case UNIT_MULTI_KILO:
				TempWholeNumber *= 1000;
				break;
			default:
				break;
		}  
		TempInteger = (int16_t) TempWholeNumber;
		TempWholeNumber -= TempInteger;
		TempWholeNumber *= 10000;
		TempTenth = (int16_t) TempWholeNumber;
		
		if (Minus) {
			TempInteger = -TempInteger;
			TempTenth = -TempTenth;
		}
		spParStat->IntPartOfNumber = TempInteger;
		spParStat->TenthPartOfNumber = TempTenth;
		spParStat->ValueOfNumber = NumVal;
	}
	else {
		spParStat->ParsingResult = SCPI_PARSING_PAR_FAIL;
	}
}
