#include "ofXMLSettings.h"

#include <vector>
#include <string>
#include <iostream>

//----------------------------------------
// a pretty useful tokenization system:
static vector<string> tokenize(const string & str, const string & delim);
static vector<string> tokenize(const string & str, const string & delim)
{
  vector<string> tokens;

  size_t p0 = 0, p1 = string::npos;
  while(p0 != string::npos)
  {
    p1 = str.find_first_of(delim, p0);
    if(p1 != p0)
    {
      string token = str.substr(p0, p1 - p0);
      tokens.push_back(token);
    }
    p0 = str.find_first_not_of(delim, p1);
  }
  return tokens;
}
//----------------------------------------


ofXMLSettings::ofXMLSettings(){
	storedHandle	= new TiXmlHandle(NULL);	
}

//---------------------------------------------------------
void ofXMLSettings::setVerbose(bool _verbose){
}

//---------------------------------------------------------
void ofXMLSettings::clear(){
	//we clear from our root level
	//this is usually the document
	//but if we are pushed  - it could
	//be all the tags inside of the pushed
	//node - including the node itself!
	
	storedHandle->ToNode()->Clear();
}

//---------------------------------------------------------
bool ofXMLSettings::loadFile(string xmlFile){
		
	xmlFile = ofToDataPath(xmlFile);

	bool loadOkay = doc.LoadFile(xmlFile.c_str());
	
	//theo removed bool check as it would
	//return false if the file exists but was
	//empty
	
	*storedHandle = TiXmlHandle(&doc);		
	return loadOkay;
}

//---------------------------------------------------------
void ofXMLSettings::saveFile(string xmlFile){
	
	xmlFile = ofToDataPath(xmlFile);
	doc.SaveFile(xmlFile.c_str());
}

//---------------------------------------------------------
void ofXMLSettings::clearTagContents(string tag, int which){
	//we check it first to see if it exists
	//otherwise setValue will make a new empty tag
	if( tagExists(tag, which) )setValue(tag, "", which);
}

//---------------------------------------------------------
void ofXMLSettings::removeTag(string  tag, int which){
	
	vector<string> tokens = tokenize(tag,":");
	
	//no tags so we return
	if( tokens.size() == 0 ) return;
		
	//grab the handle from the level we are at
	//normally this is the doc but could be a pushed node
	TiXmlHandle tagHandle = *storedHandle;
	
	if(which < 0) which = 0;
	
	for(int x=0;x<tokens.size();x++){
	
		//we only support multi tags
		//with same name at root level
		if(x > 0) which = 0;
			
		TiXmlHandle isRealHandle = tagHandle.ChildElement( tokens.at(x), which);
		
		if ( !isRealHandle.Node() ) break;
		else{
			if (x == tokens.size()-1){
				//if we are at the last tag and it exists 
				//we use its parent to remove it - haha
				tagHandle.ToNode()->RemoveChild( isRealHandle.ToNode() );
			}
			tagHandle = isRealHandle;
		}
	}
	
	tokens.clear();
}

//---------------------------------------------------------
int ofXMLSettings::getValue(string tag, int defaultValue, int which){
	char * tempStr = new char[MAX_TAG_VALUE_LENGTH_IN_CHARS];
	memset(tempStr, 0, MAX_TAG_VALUE_LENGTH_IN_CHARS);
	int returnValue = defaultValue;

	if (readTag(tag, tempStr, which)){
		returnValue = atoi(tempStr);
	} 
	delete tempStr;
	return returnValue;
}

//---------------------------------------------------------
float ofXMLSettings::getValue(string tag, double defaultValue, int which){
	char * tempStr = new char[MAX_TAG_VALUE_LENGTH_IN_CHARS];
	memset(tempStr, 0, MAX_TAG_VALUE_LENGTH_IN_CHARS);
	float returnValue = defaultValue;

	if (readTag(tag, tempStr, which)){
		returnValue = atof(tempStr);
	} 
	delete tempStr;
	return returnValue;
}

//---------------------------------------------------------
string ofXMLSettings::getValue(string tag, string defaultValue, int which){
	
	// lots of char *, string kung-fu here...
	
	char * tempStr = new char[MAX_TAG_VALUE_LENGTH_IN_CHARS];
	memset(tempStr, 0, MAX_TAG_VALUE_LENGTH_IN_CHARS);
	char * returnPtr = (char *) defaultValue.c_str();
	if (readTag(tag, tempStr, which)){
		returnPtr = tempStr;
	} 
	string returnString(returnPtr);
	delete tempStr;
	return returnString;
}

//---------------------------------------------------------
bool ofXMLSettings::readTag(string  tag, char * valueString, int which){

	vector<string> tokens = tokenize(tag,":");
		
	TiXmlHandle tagHandle = *storedHandle;
	for(int x=0;x<tokens.size();x++){
		if(x == 0)tagHandle = tagHandle.ChildElement(tokens.at(x), which);
		else tagHandle = tagHandle.FirstChildElement( tokens.at(x) );
	}

	// once we've walked, let's get that value...
	TiXmlHandle valHandle = tagHandle.Child( 0 );
    
    //now, clear that vector! 
	tokens.clear();		
	
    // if that value is really text, let's get the value out of it ! 
    if (valHandle.Text()){
    	int maxLen = MIN(MAX_TAG_VALUE_LENGTH_IN_CHARS, strlen(valHandle.Text()->Value()));
    	memcpy(valueString, valHandle.Text()->Value(), maxLen);
    	return true;	
    }  else {
		return false;
	}
}


//---------------------------------------------------------
bool ofXMLSettings::pushTag(string  tag, int which){
	
	int pos = tag.find(":");
		
	if(pos > 0){
		tag = tag.substr(0,pos);
	}	
		
	//we only allow to push one tag at a time.
	TiXmlHandle isRealHandle = storedHandle->ChildElement(tag, which);

	if( isRealHandle.Node() ){		
		*storedHandle = isRealHandle;
		level++;
		return true;
	}else{
		printf("pushTag - tag not found\n");
	}
	
	return false;
}

//---------------------------------------------------------
int ofXMLSettings::popTag(){
	
	if(level > 1){
		TiXmlHandle parent( (storedHandle->ToNode() )->Parent() );
		*storedHandle = parent;
		level--;
	}else{
		*storedHandle = TiXmlHandle(&doc);		
		level = 0;
	}
	
	return level;
}

//---------------------------------------------------------
int ofXMLSettings::getPushLevel(){
	return level;
}

//---------------------------------------------------------
bool ofXMLSettings::tagExists(string  tag, int which){
	
	vector<string> tokens = tokenize(tag,":");
	
	bool found = false;
		
	//grab the handle from the level we are at
	//normally this is the doc but could be a pushed node
	TiXmlHandle tagHandle = *storedHandle;
	
	if(which < 0) which = 0;
	
	for(int x=0;x<tokens.size();x++){
	
		//we only support multi tags
		//with same name at root level
		if(x > 0) which = 0;
			
		TiXmlHandle isRealHandle = tagHandle.ChildElement( tokens.at(x), which);
				
		//as soon as we find a tag that doesn't exist
		//we return false;
		if ( !isRealHandle.Node() ){
			found = false;
			break;
		}
		else{
			found = true;
			tagHandle = isRealHandle;
		}
	}

	tokens.clear();

	return found;
}


//---------------------------------------------------------
int ofXMLSettings::getNumTags(string  tag){
	//this only works for tags at the current root level
		
	int pos = tag.find(":");
		
	if(pos > 0){
		tag = tag.substr(0,pos);
	}	
		
	//grab the handle from the level we are at
	//normally this is the doc but could be a pushed node
	TiXmlHandle tagHandle = *storedHandle;

	int count = 0;
	
	//ripped from tinyXML as doing this ourselves once is a LOT! faster
	//than having this called n number of times in a while loop - we go from n*n iterations to n iterations

	TiXmlElement* child = ( storedHandle->FirstChildElement( tag ) ).Element();
	for (count = 0; child; child = child->NextSiblingElement( tag ), ++count){
		//nothing
	}	

	return count;
}



//---------------------------------------------------------
int ofXMLSettings::writeTag(string  tag, char * valueStr, int which){
	
	vector<string> tokens = tokenize(tag,":");
	
	// allocate then clean up :
	TiXmlElement ** elements = new TiXmlElement*[tokens.size()];
	for(int x=0;x<tokens.size();x++){
		elements[x] = new TiXmlElement(tokens.at(x));
	}
	
	TiXmlText Value(valueStr);			
	
	// search our way up - do these tags exist?
	// find the first that DOESNT exist, then move backwards...
	TiXmlHandle tagHandle = *storedHandle;
	
	bool addNewTag = false;
	if(which == -1)addNewTag = true;
		
	for(int x=0;x<tokens.size();x++){
	
		if( x > 0 ){
			//multi tags of same name 
			//only for the root level
			which = 0;
			addNewTag = false;
		}
	
		TiXmlHandle isRealHandle = tagHandle.ChildElement( tokens.at(x), which);
				
		if ( !isRealHandle.Node() ||  addNewTag){
			
			for(int i=tokens.size()-1;i>=x;i--){
				if (i == tokens.size()-1){
					elements[i]->InsertEndChild(Value);
				} else {
					elements[i]->InsertEndChild(*(elements[i+1]));
				}
			}
						
			tagHandle.ToNode()->InsertEndChild(*(elements[x]));
			
			break;
			
		} else {
			 tagHandle = isRealHandle;
			 if (x == tokens.size()-1){
				// what we want to change : TiXmlHandle valHandle = tagHandle.Child( 0 );
				tagHandle.ToNode()->Clear();
				tagHandle.ToNode()->InsertEndChild(Value);
			}
		}
	}
	
	
	//lets count how many tags with our name exist so we can return an index
	
	//ripped from tinyXML as doing this ourselves once is a LOT! faster
	//than having this called n number of times in a while loop - we go from n*n iterations to n iterations
	int numSameTags;
	TiXmlElement* child = ( storedHandle->FirstChildElement( tokens.at(0) ) ).Element();
	for (numSameTags = 0; child; child = child->NextSiblingElement( tokens.at(0) ), ++numSameTags){
		//nothing
	}
	
	//now, clear that vector! 
	tokens.clear();		

	return numSameTags;
}

//---------------------------------------------------------
int ofXMLSettings::setValue(string tag, int value, int which){
	char valueStr[255];
	sprintf(valueStr, "%i", value);
	int tagID = writeTag(tag, valueStr, which) -1;
	return tagID;	
}

//---------------------------------------------------------
int ofXMLSettings::setValue(string  tag, double value, int which){
	char valueStr[255];
	sprintf(valueStr, "%f", value);
	int tagID = writeTag(tag, valueStr, which) -1;
	return tagID;
}

//---------------------------------------------------------
int ofXMLSettings::setValue(string tag, string value, int which){
	int tagID = writeTag(tag, (char *)value.c_str(), which) -1;
	return tagID;
}

//---------------------------------------------------------
int ofXMLSettings::addValue(string tag, int value){
	char valueStr[255];
	sprintf(valueStr, "%i", value);
	int tagID = writeTag(tag, valueStr, -1) -1;
	return tagID;
}

//---------------------------------------------------------
int ofXMLSettings::addValue(string  tag, double value){
	char valueStr[255];
	sprintf(valueStr, "%f", value);
	int tagID = writeTag(tag, valueStr, -1) -1;
	return tagID;
}

//---------------------------------------------------------
int ofXMLSettings::addValue(string tag, string value){
	int tagID = writeTag(tag, (char *)value.c_str(), -1) -1;
	return tagID;	
}

//---------------------------------------------------------
int ofXMLSettings::addTag(string tag){
	int tagID = writeTag(tag, "", -1) -1;
	return tagID;	
}


