
#include "SSXMLParser.h"

// Returns next tag or empty string (if it doesn't exit)
// deletes from input!
string SSXMLParser::getElement()
{
	int lt = finds("<");
	int gt = finds(">", lt, input.length());

	string ret = "";

	if ((input[gt-1] == '/') || ((char)input[gt-1] == '?'))
	{
		// copy & destroy
		if (gt > lt)
		{
			ret = input.substr(lt, (gt-lt)+1);
			input.erase(0, gt+1);
		}
	}
	else
	{
		string tagName = findName(lt, gt);
		int endlt = findEndTag(tagName, gt);
		int endgt = finds(">", endlt, input.length());
		// copy & destroy
		if (endgt > lt)
		{
			ret = input.substr(lt, (endgt-lt)+1);
			input.erase(0, endgt+1);
		}
	}

	return ret;
}

// Returns next start tag or empty string (if it doesn't exit), ignores </tags>
// deletes from input!
string SSXMLParser::getTag()
{
	int lt = finds("<");
	int gt = finds(">", lt, input.length());

	// copy & destroy
	string ret = "";
	if (gt > lt)
	{
		ret = input.substr(lt, (gt-lt)+1);
		input.erase(0, gt+1);
	}

	return ret;
}

// Returns position of next end tag or -1 (if it doesn't exit)
int SSXMLParser::findEndTag(const string& name, int from)
{
	return finds(input, "</"+name, from, input.length());
}

int SSXMLParser::findEndTag(const string& inp, const string& name, int from)
{
	return finds(inp, "</"+name, from, inp.length());
};

// Finds first XML name
string SSXMLParser::findName()
{
	return findName(input, 0, input.length());
}

string SSXMLParser::findName(const string& inp)
{
	return findName(inp, 0, inp.length());
}

// Finds an XML name from the position and before specified position (length)
string SSXMLParser::findName(int from, int before)
{
	return findName(input, from, before);
}

string SSXMLParser::findName(const string& inp, int from, int before)
{
	if (from < 0)
	{
		from = 0;
	}
	if (before > inp.length())
	{
		before = inp.length();
	}

	int s = 0;      // initial state - not in the string
	string found = "";

	// iterate
	for (int i = from; i < before; i++)
	{
		switch (s)
		{
		case 0: // state nothing found
			if (isName(inp[i]))
			{
				found = inp[i];
				s = 1;
				break;
			}
			break;
		case 1: // state found some name character
			if (isName(inp[i]))
			{
				found += inp[i];
				break;
			}
			else
			{
				return found;  // found, finito
			}
			break;
		}
	}

	return "";     // not found
}

// Finds first XML "string"
string SSXMLParser::findString()
{
	return findString(input, 0, input.length());
}

string SSXMLParser::findString(const string& inp)
{
	return findString(inp, 0, inp.length());
}

// Finds an XML "string" from the position and before specified position (length)
string SSXMLParser::findString(int from, int before)
{
	return findString(input, from, before);
}

string SSXMLParser::findString(const string& inp, int from, int before)
{
	// initial state - not in the tag
	int s = 0;
	string str = "";

	// iterate
	for (int i = from; i < before; i++)
	{
		switch (s) {
			case 0 : { // nothing
				// ingnoring anything but "strings"
				if     (inp[i] == '\\') { s = 1; break; }
				else if(inp[i] == '"' ) { s = 5; break; }
			} break;
			case 1 : { // found "\" - special character
				s = 0;  // don't care about it
			} break;
			case 5 : { // in "string"
				if     (inp[i] == '"' ) { return str; } // leaving the string
				else {
					str += inp[i];
					if(inp[i] == '\\') { s = 6; break; }
				}
			} break;
			case 6 : { // found "\" - special character in "string"
				str += inp[i];
				s = 5;  // don't care about it
			} break;
		}
	}

	return str;
}


// Returns value of an attribute or empty string (if it doesn't exit)
string SSXMLParser::findAttrib(const string& name)
{
	return findAttrib(input, name);
}

string SSXMLParser::findAttrib(const string& inp, const string& name)
{
	int gt = finds(inp, ">");          // find end of the elem.
	int pos = finds(inp, name, 0, gt); // find the attribute name
	// TODO: check if = present?
	return findString(inp, pos, gt);   // find first string after the attribute name

	// TODO: if none found or found before second = - use findName after =
}

// Finds first position of a substring ignoring "strings" and whitespace
int SSXMLParser::finds(const string& substr)
{
	return finds(input, substr, 0, input.length(), false);
}

int SSXMLParser::finds(const string& inp, const string& substr)
{
	return finds(inp, substr, 0, inp.length(), false);
}

// Finds first position of a substring ignoring "strings" and whitespace
// from the position and before specified position (length)
int SSXMLParser::finds(const string& substr, int from, int before, bool inString)
{
	return finds(input, substr, from, before, inString);
}

int SSXMLParser::finds(const string& inp, const string& substr, int from, int before, bool inString)
{
	if (from < 0) from = 0;
	if (before > inp.length()) before = inp.length();
    int ssl = substr.length();
    
    int s = 0;      // initial state - not in the string
    if (inString) s = 5;    // it is supposed to start in a string
    
    // searching for some special character?
    bool findQuotes = (substr.compare("\"") == 0);

    // iterate
    for(int i = from; i < before; i++) {
        
        switch (s) {
            case 0 : { // nothing
				if (isspace(inp[i])) { break; }  // ignoring white space
				else if(inp[i] == '\\') { s = 1; break; }
				else if(inp[i] == '"' ) { s = 5; if (findQuotes) return i; else break; }
				else if((i+ssl) > inp.length()) return -1;    // cannot find - exit
                else if(substr.compare(input.substr(i, ssl)) == 0) { return i; }  // found, finish!
            } break;
            case 1 : { // found "\" - special character
                s = 0;  // don't care about it
			} break;
			case 5 : { // in "string"
				if     (inp[i] == '"' ) { s = 0; if (findQuotes) return i; else break; } // leaving the string
				else if(inp[i] == '\\') { s = 6; break; } // not searching in strings
			} break;
			case 6 : { // found "\" - special character in "string"
				s = 5;  // don't care about it
			} break;
		}
	}

	return -1;     // not found
}

// Transforms lowercase, ignoring "strings"
void SSXMLParser::toLower()
{
	toLower(input);
}

// Transforms lowercase, ignoring "strings"
void SSXMLParser::toLower(string& inp)
{
	// std::transform(inp.begin(), inp.end(), inp.begin(), (int(*)(int))std::tolower);

	// initial state - not in the tag
	int s = 0;

	// iterate
	for (int i = 0; i < inp.length(); i++)
	{
		switch (s)
		{
		case 0: // nothing
			if (isspace(inp[i]))
			{ // ignoring white space
				break;
			}
			else if (inp[i] == '\\')
			{
				s = 1;
				break;
			}
			else if (inp[i] == '"' )
			{
				s = 5;
				break;
			}
			else inp[i] = tolower(inp[i]);
			break;
		case 1: // found "\" - special character
			s = 0; // don't care about it
			break;
		case 5: // in "string"
			if      (inp[i] == '"' )
			{ // leaving the string
				s = 0;
				break;
			}
			else if (inp[i] == '\\')
			{
				s = 6;
				break;
			}
			break;
		case 6: // found "\" - special character in "string"
			s = 5; // don't care about it
			break;
		}
	}
}

