/*
 *  stringUtils.cpp
 *  StringUtils
 *
 *  Created by Robert Delaney on 1/7/11.
 *  Copyright 2011 Bob Delaney's Science Software. All rights reserved.
 *
 */

#include "stringUtils.h"
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <iostream>

// starting at index n, finds first occurrence of char ch in theString
// returns true and index of ch when found, else returns false
static bool FindChar(long& index, const char *theString, const char ch, long n)
{
	long length, i;
	
	length = strlen(theString);
	if(!length || ch==0)
		return false;
	
	for(i=n;i<length;i++)
	{
		if(theString[i]==ch)
		{
			index = i;
			return true;
		}
	}
	
	return false;
	
}/* FindChar */


// starting at index n, finds first occurrence of sep in theString
// returns true and index of sep when found, else returns false
static bool FindSep(long& index, const char *theString, const char *sep, long n)
{
	long length, len, i, j, chIndex;
	char chSep; // first character of sep
	
	length = strlen(theString);
	if(!length)
		return false;
	
	len = strlen(sep);
	if(!len)
		return false;
	
	if((length-n)<len)
		return false;
	
	chSep = sep[0];
	
	// move through theString with i
	i = n;
	while(i<=(length-len))
	{
		if(FindChar(chIndex, theString, chSep, i))
		{
			if((chIndex+len)<=length)
			{
				for(j=1;j<len;j++)
				{
					if(!(theString[chIndex+j]==sep[j]))
						break;
				}
				if(j==len)
				{
					index = chIndex;
					return true;
				}
			}
		}
		else
			return false;

		i = chIndex + 1;
	}
	
	return false;
	
}/* FindSep */


// returns the number of fields in "theString" that are separated by the string "sep".
// this number is one more than the number of occurrences of sep in theString.
long CountFields(const char *theString, const char *sep)
{
	long length, len, i, numEntries, index;
	
	length = strlen(theString);
	if(!length)
		return 1;
	
	len = strlen(sep);
	if(!len)
		return 1;
	
	if(length<len)
		return 1;
	
	numEntries = 1;
	
	// move through theString with i
	i = 0;
	while(i<(length-len))
	{
		if(FindSep(index, theString, sep, i))
		{
			numEntries++;
			i = index + 1;
		}
		else
		{
			return numEntries;
		}
	}
	
	return numEntries;
	
}/* CountFields */


// returns string field pointed to by fieldNum. The first field is numbered 1
char* NthField(const char *theString, const char *sep, long fieldNum)
{
	long numEntries, length, len, lenField, index, prevIndex, i, j;
	char *theField;
	
	// make theField a null string
	theField = (char*)malloc(sizeof(char));
	theField[0] = 0;
	
	length = strlen(theString);
	len = strlen(sep);
	
	numEntries = CountFields(theString, sep);
	if(numEntries==1)
	{
		theField = (char*)realloc(theField, (length+1)*sizeof(char));
		strcpy(theField, theString);
		return theField;
	}
	
	// special cases: fieldNum = 1 or numEntries
	
	if(fieldNum==1)
	{
		// find the first occurrence of sep
		if(!FindSep(index, theString, sep, 0))
		{
			return theField;
		}
		
		// theField is everything to the left of sep
		if(index==0)
		{
			return theField;
		}
		
		theField = (char*)realloc(theField, (index+1)*sizeof(char));
		for(i=0;i<index;i++)
			theField[i] = theString[i];
		theField[index] = 0;
		return theField;
	}
	
	if(fieldNum==numEntries)
	{
		// find index associated with fieldNum-1
		j = 0;
		for(i=0;i<fieldNum-1;i++)
		{
			if(FindSep(index, theString, sep, j))
			{
				j = index + 1;
			}
			else
			{
				return theField;
			}
		}
		
		// theField is everything to the right of sep
		if(index==(length-len))
		{
			return theField;
		}
		
		lenField = length-index-len;
		theField = (char*)realloc(theField, (lenField+1)*sizeof(char));
		
		for(i=0;i<lenField;i++)
			theField[i] = theString[i+index+len];
		theField[lenField] = 0;
		return theField;
	}
	
	
	// now find the index associated with fieldNum and prevIndex associated wirh fieldNum-1
	j = 0;
	for(i=0;i<fieldNum;i++)
	{
		if(FindSep(index, theString, sep, j))
		{
			j = index + 1;
		}
		else
		{
			return theField;
		}
		if(i==fieldNum-2)
			prevIndex = index;
	}

	// theField is between the previous sep and the sep at index
	
	lenField = index-prevIndex-len;
	if(lenField<=0)
	{
		return theField;
	}
	
	theField = (char*)realloc(theField, (lenField+1)*sizeof(char));
	
	for(i=0;i<lenField;i++)
		theField[i] = theString[i+prevIndex+len];
	theField[lenField] = 0;
	return theField;
	
}/* NthField */


// verify numString with no decimal point and no exponent, i.e. is it an integer string?
// it is assumed that spaces have been removed
bool VerifyNumString(const char *numString)
{
	long	i, length;
	char	ch;
	
	length = strlen(numString);
	
	if(!length)
		return false;
	
	ch = numString[0];
	if(!(ch=='+' || ch=='-' || (ch>='0' && ch<='9')))
		return false;
	
	for(i=1;i<length;i++)
	{
		ch = numString[i];
		if(!(ch>='0' && ch<='9'))
			return false;
	}
	
	return true;
	
}/* VerifyNumString */


// verify numString with possible decimal point and without exponent
bool VerifyNumDPString(const char *numString)
{
	long	length, len, i, j, numFields;
	char	*copyString, *beforeDP, *afterDP, ch, sep[2]=".";
	
	length = strlen(numString);
	if(!length)
		return false;
	
	copyString = (char*)malloc((length+1)*sizeof(char));
	
	// copy without spaces
	j = 0;
	for(i=0;i<length;i++)
	{
		ch = numString[i];
		if(!(ch==' '))
			copyString[j++] = ch;
	}
	copyString[j] = 0;
	
	length = strlen(copyString);
	if(!length)
	{
		free(copyString);
		return false;
	}
	
	// find numFields with "." as separator
	numFields = CountFields(copyString, sep);
	
	if(numFields>2)
	{
		free(copyString);
		return false;
	}
		
	beforeDP = NthField(copyString, sep, 1);
	// this can be null, +, -, or a possible integer
	
	len = strlen(beforeDP);
	if(len)  // bypass if len is zero since that's acceptable
	{
		// not null
		if(len==1)
		{
			ch = beforeDP[0];
			if(!(ch=='+' || ch=='-' || (ch>='0' && ch<='9')))
			{
				free(beforeDP);
				free(copyString);
				return false;
			}
		}
		else
		{
			// now len>1 so need to verify we have an integer
			if(!VerifyNumString(beforeDP))
			{
				free(beforeDP);
				free(copyString);
				return false;
			}
		}
	}
	
	if(numFields==2)
	{
		afterDP = NthField(copyString, sep, 2);
		len = strlen(afterDP);
		if(len>0)  // OK if len is zero
		{
			// not null, but must make sure that first character is not + or - since VerifyNumString accepts them
			ch = afterDP[0];
			if(ch=='+' || ch=='-')
			{
				free(beforeDP);
				free(afterDP);
				free(copyString);
				return false;
			}
			
			if(!VerifyNumString(afterDP))
			{
				free(beforeDP);
				free(afterDP);
				free(copyString);
				return false;
			}
		}
	}
	
	free(beforeDP);
	if(numFields==2)
		free(afterDP);
	free(copyString);
	return true;
	
}/* VerifyNumDPString */


// verify numString with possible exponent
bool VerifyNumExpString(const char *numString)
{
	long	length, i, j, numFields;
	char	*copyString, *mantissa, *exponent, ch, sep[2]="e";
	
	length = strlen(numString);
	if(!length)
		return false;
	
	copyString = (char*)malloc((length+1)*sizeof(char));
	
	// copy without spaces, and convert possible E to e
	j = 0;
	for(i=0;i<length;i++)
	{
		ch = numString[i];
		if(!(ch==' '))
			copyString[j++] = tolower(ch);
	}
	copyString[j] = 0;
	
	length = strlen(copyString);
	if(!length)
	{
		free(copyString);
		return false;
	}
	
	// find numFields with "e" as separator
	numFields = CountFields(copyString, sep);
	
	if(numFields>2)
	{
		free(copyString);
		return false;
	}
	
	mantissa = NthField(copyString, sep, 1);
	if(!VerifyNumDPString(mantissa))
	{
		free(mantissa);
		free(copyString);
		return false;
	}
	
	if(numFields==2)
	{
		exponent = NthField(copyString, sep, 2);
		if(!VerifyNumString(exponent))
		{
			free(mantissa);
			free(exponent);
			free(copyString);
			return false;
		}
	}
	
	free(mantissa);
	if(numFields==2)
		free(exponent);
	free(copyString);
	return true;
	
}/* VerifyNumExpString */


// verify numString and
// if true mantissa and exponent are valid number strings
// if false mantissa and exponent are malloced and you can use free
bool VerifyNumExpString2(char*& mantissa, char*& exponent, const char *numString)
{
	long	length, i, j, numFields;
	char	*copyString, ch, sep[2]="e";
	
	// init as zero strings
	mantissa = (char*)malloc(2*sizeof(char));
	mantissa[0] = '0';
	mantissa[1] = 0;
	exponent = (char*)malloc(2*sizeof(char));
	exponent[0] = '0';
	exponent[1] = 0;
	
	length = strlen(numString);
	if(!length)
		return false;
	
	copyString = (char*)malloc((length+1)*sizeof(char));
	
	// copy without spaces, and convert possible E to e
	j = 0;
	for(i=0;i<length;i++)
	{
		ch = numString[i];
		if(!(ch==' '))
			copyString[j++] = tolower(ch);
	}
	copyString[j] = 0;
	
	length = strlen(copyString);
	if(!length)
	{
		free(copyString);
		return false;
	}
	
	// find numFields with "e" as separator
	numFields = CountFields(copyString, sep);
	
	if(numFields>2)
	{
		free(copyString);
		return false;
	}
	
	free(mantissa);
	mantissa = NthField(copyString, sep, 1);
	if(!VerifyNumDPString(mantissa))
	{
		free(copyString);
		return false;
	}
	
	if(numFields==2)
	{
		free(exponent);
		exponent = NthField(copyString, sep, 2);
		if(!VerifyNumString(exponent))
		{
			free(copyString);
			return false;
		}
	}
	
	free(copyString);
	return true;
							   
}/* VerifyNumExpString2 */


// verify numString as possible fraction "num/den", it does not give false if den would convert to zero
// if not a fraction but num is verified it returns true
bool VerifyNumFracString(const char *numString)
{
	long	length, i, j, numFields;
	char	*copyString, *num, *den, ch, sep[2]="/";
	
	length = strlen(numString);
	if(!length)
		return false;
	
	copyString = (char*)malloc((length+1)*sizeof(char));
	
	// copy without spaces
	j = 0;
	for(i=0;i<length;i++)
	{
		ch = numString[i];
		if(!(ch==' '))
			copyString[j++] = ch;
	}
	copyString[j] = 0;
	
	length = strlen(copyString);
	if(!length)
	{
		free(copyString);
		return false;
	}
	
	// find numFields with "/" as separator
	numFields = CountFields(copyString, sep);
	
	if(numFields>2)
	{
		free(copyString);
		return false;
	}
	
	num = NthField(copyString, sep, 1);
	
	if(!VerifyNumExpString(num))
	{
		free(num);
		free(copyString);
		return false;
	}
	
	if(numFields==2)
	{
		den = NthField(copyString, sep, 2);
		if(!VerifyNumExpString(den))
		{
			free(num);
			free(den);
			free(copyString);
			return false;
		}
	}
	
	free(num);
	if(numFields==2)
		free(den);
	free(copyString);
	return true;
	
}/* VerifyNumFracString */


// , it does not give false if den would convert to zero
// if not a fraction but num is verified it returns true and den = '1'
// if true gives validated num and den strings
bool VerifyNumFracString2(char*& num, char*& den, const char *numString)
{
	long	length, i, j, numFields;
	char	*copyString, ch, sep[2]="/";
	
	num = (char*)malloc(2*sizeof(char));
	num[0] = '0';
	num[1] = 0;
	den = (char*)malloc(2*sizeof(char));
	den[0] = '1';
	den[1] = 0;
	
	length = strlen(numString);
	if(!length)
		return false;
	
	copyString = (char*)malloc((length+1)*sizeof(char));
	
	// copy without spaces
	j = 0;
	for(i=0;i<length;i++)
	{
		ch = numString[i];
		if(!(ch==' '))
			copyString[j++] = ch;
	}
	copyString[j] = 0;
	
	length = strlen(copyString);
	if(!length)
	{
		free(copyString);
		return false;
	}
	
	// find numFields with "/" as separator
	numFields = CountFields(copyString, sep);
	
	if(numFields>2)
	{
		free(copyString);
		return false;
	}
	
	free(num);
	num = NthField(copyString, sep, 1);
	
	if(!VerifyNumExpString(num))
	{
		free(copyString);
		return false;
	}
	
	if(numFields==2)
	{
		free(den);
		den = NthField(copyString, sep, 2);
		if(!VerifyNumExpString(den))
		{
			free(copyString);
			return false;
		}
	}

	free(copyString);
	return true;
	
}/* VerifyNumFracString2 */


// verify numString as possible complex with fractions "(num/den,num/den)"
bool VerifyNumComplexFracString(const char *numString)
{
	long	length, i, j, numFields;
	char	*copyString, *re, *im, ch, sep[2]=",";
	
	length = strlen(numString);
	if(!length)
		return false;
	
	copyString = (char*)malloc((length+1)*sizeof(char));
	
	// copy without spaces or parentheses
	j = 0;
	for(i=0;i<length;i++)
	{
		ch = numString[i];
		if(!(ch==' ' || ch=='(' || ch==')'))
			copyString[j++] = ch;
	}
	copyString[j] = 0;
	
	length = strlen(copyString);
	if(!length)
	{
		free(copyString);
		return false;
	}
	
	// find numFields with "," as separator
	numFields = CountFields(copyString, sep);
	
	if(numFields>2)
	{
		free(copyString);
		return false;
	}
	
	re = NthField(copyString, sep, 1);
	
	if(!VerifyNumFracString(re))
	{
		free(re);
		free(copyString);
		return false;
	}
	
	if(numFields==2)
	{
		im = NthField(copyString, sep, 2);
		if(!VerifyNumFracString(im))
		{
			free(re);
			free(im);
			free(copyString);
			return false;
		}
	}
	
	free(re);
	if(numFields==2)
		free(im);
	free(copyString);
	return true;
	
}/* VerifyNumComplexFracString */


// verify numString as possible complex with fractions "(re,im)=(num/den,num/den)"
bool VerifyNumComplexFracString2(char*& re, char*& im, const char *numString)
{
	long	length, i, j, numFields;
	char	*copyString, ch, sep[2]=",";
	
	re = (char*)malloc(4*sizeof(char));
	re[0] = '0';
	re[1] = '/';
	re[2] = '1';
	re[3] = 0;
	im = (char*)malloc(4*sizeof(char));
	im[0] = '0';
	im[1] = '/';
	im[2] = '1';
	im[3] = 0;
	
	length = strlen(numString);
	if(!length)
		return false;
	
	copyString = (char*)malloc((length+1)*sizeof(char));
	
	// copy without spaces or parentheses
	j = 0;
	for(i=0;i<length;i++)
	{
		ch = numString[i];
		if(!(ch==' ' || ch=='(' || ch==')' || ch=='+'))
			copyString[j++] = ch;
	}
	copyString[j] = 0;
	
	length = strlen(copyString);
	if(!length)
	{
		free(copyString);
		return false;
	}
	
	// find numFields with "," as separator
	numFields = CountFields(copyString, sep);
	
	if(numFields>2)
	{
		free(copyString);
		return false;
	}
	
	free(re);
	re = NthField(copyString, sep, 1);
	
	if(!VerifyNumFracString(re))
	{
		free(copyString);
		return false;
	}
	
	if(numFields==2)
	{
		free(im);
		im = NthField(copyString, sep, 2);
		if(!VerifyNumFracString(im))
		{
			free(copyString);
			return false;
		}
	}
	
	free(copyString);
	return true;
	
}/* VerifyNumComplexFracString2 */

// translates the individual bytes of a Cstring into hex ASCII
char* cstring2Hex(const char* inputStr)
{
    char        *outStr;
    long        i, length;
    char        ch;
    char        hexDigits[4];
    char        space[2];
    
    strcpy(space, " ");
    
    // sprintf(hexStr,"%0x",ch);
    
    length = strlen(inputStr);
    
    outStr = (char*)malloc((3*length+10)*sizeof(char));
    outStr[0] = 0;
    
    for(i=0;i<length;++i)
    {
        ch = inputStr[i];
        sprintf(hexDigits,"%0x",ch);
        if(strlen(hexDigits)==1)
        {
            hexDigits[1] = hexDigits[0];
            hexDigits[0] = '0';
            hexDigits[2] = 0;
        }
        strcat(hexDigits, space);
        strcat(outStr, hexDigits);
    }
    
    return outStr;
    
}/* cstring2Hex */


