#include "ofConstants.h"
#include "ofxXmlPoco.h"
#include "Poco/AutoPtr.h"

using namespace std;

ofxXmlPoco::~ofxXmlPoco(){
	releaseAll();
}


ofxXmlPoco::ofxXmlPoco(const string & path){
	document = new Poco::XML::Document(); // we create this so that they can be merged later
	element = document->documentElement();
	load(path);
}

ofxXmlPoco::ofxXmlPoco(const ofxXmlPoco & rhs){
    document = new Poco::XML::Document();
    Poco::XML::Node *n = document->importNode(rhs.getPocoDocument()->documentElement(), true);
    document->appendChild(n);
    
    element = document->documentElement();
}

const ofxXmlPoco & ofxXmlPoco::operator=(const ofxXmlPoco & rhs){
	if(&rhs == this){
		return *this;
	}

	releaseAll();

	document = (Poco::XML::Document *)rhs.document->cloneNode(true);
	element = document->documentElement();
	return *this;
}


ofxXmlPoco::ofxXmlPoco(){
	document = new Poco::XML::Document(); // we create this so that they can be merged later
	element = document->documentElement();
}


bool ofxXmlPoco::load(const std::filesystem::path & path){
	ofFile file(path, ofFile::ReadOnly);
	if(!file.exists()){
		ofLogError("ofxXmlPoco") << "couldn't load, \"" << file.getFileName() << "\" not found";
		return false;
	}
	ofBuffer xmlBuffer(file);
	return loadFromBuffer(xmlBuffer);
}


bool ofxXmlPoco::save(const std::filesystem::path & path){
    ofBuffer buffer;
    buffer.set(toString());
    ofFile file(path, ofFile::WriteOnly);
    return file.writeFromBuffer(buffer);
}

int ofxXmlPoco::getNumChildren() const{
	if(!element){
		return 0;
	}

    int numberOfChildren = 0;
    Poco::XML::NodeList *list = element->childNodes();
    
    for(unsigned long i=0; i < list->length(); i++){
        if(list->item(i) && list->item(i)->nodeType() == Poco::XML::Node::ELEMENT_NODE){
            numberOfChildren++;
        }
    }
    return numberOfChildren;
}

int ofxXmlPoco::getNumChildren(const string& path) const{
	if(!element){
		return 0;
	}

    int numberOfChildren = 0;
    Poco::XML::NodeList *list = element->childNodes();

    for(unsigned long i=0; i < list->length(); i++){
        if(list->item(i) && list->item(i)->nodeType() == Poco::XML::Node::ELEMENT_NODE){
            string nodeName = list->item(i)->localName();
            if(path.compare(nodeName) == 0){
                numberOfChildren++;
            }
        }
    }

    return numberOfChildren;
}

string ofxXmlPoco::toString() const{
    ostringstream stream;

    Poco::XML::DOMWriter writer;
    writer.setOptions(Poco::XML::XMLWriter::PRETTY_PRINT);
    if(document){
        try{
            writer.writeNode( stream, getPocoDocument() );
        }catch( exception & e ){
            ofLogError("ofxXmlPoco") << "toString(): " << e.what();
        }
    } else if(element){
        element->normalize();
        writer.writeNode( stream, element );
    }

    string tmp = stream.str();

    // don't know how else to get rid of the hidden <#text></#text> nodes :/
    ofStringReplace(tmp, "<#text>", "");
    ofStringReplace(tmp, "</#text>", "");

    return tmp;
}

void ofxXmlPoco::addXml(ofxXmlPoco& xml, bool copyAll){
    Poco::XML::Node *n = NULL;
    if(copyAll){
        n = document->importNode(xml.getPocoDocument()->documentElement(), true);
    }else{
        if(xml.getPocoElement() == 0 || xml.getPocoElement() == xml.getPocoDocument()->documentElement()){
            n = document->importNode(xml.getPocoDocument()->documentElement(), true);
        }else{
            n = document->importNode( xml.getPocoElement(), true);
        }
    }

    // we have an element, i.e. the document has child nodes
    // or we don't, so append it directly to the document
    if(element){
        element->appendChild(n);
    }else{
        document->appendChild(n);
    }
}

bool ofxXmlPoco::addChild(const string& path){
    vector<string> tokens;

    if(path.find('/') != string::npos){
        tokens = tokenize(path, "/");
    }
    
    // is this a tokenized tag?
    if(tokens.size() > 1){
        // don't 'push' down into the new nodes
        Poco::XML::Element *el = element;

        vector<Poco::XML::Element*> toBeReleased;

		for(std::size_t i = 0; i < tokens.size(); i++){
            Poco::XML::Element *pe = getPocoDocument()->createElement(tokens.at(i));
            el->appendChild(pe);
            toBeReleased.push_back(pe);
            el = pe;
        }

        if(element){
            element->appendChild(el);
        }else{
            element = el;
        }

        return true;

    }else{
        Poco::XML::Element* pe = getPocoDocument()->createElement(path);

        if(element){
            element->appendChild(pe);
        }else{
            document->appendChild(pe);
            element = document->documentElement();
        }
    }
    return true;
}

string ofxXmlPoco::getValue() const{
	//if we don't have a DOM element, return the default value
	if(!element){
		return "";
	}

	// firstChild() may return a NULL pointer
	if(NULL == element->firstChild()){
		// return default value in this case
		return "";
	}
	// no NULL pointer -> save to call nodeType()
	if(element->firstChild()->nodeType() == Poco::XML::Node::TEXT_NODE) {
		return element->innerText();
	}
	return "";
}

string ofxXmlPoco::getValue(const string & path) const{
	return getValue <string>(path, "");
}


int ofxXmlPoco::getIntValue() const {
	return ofToInt(getValue());
}


int ofxXmlPoco::getIntValue(const string & path) const {
	return getValue <int>(path, 0);
}


float ofxXmlPoco::getFloatValue() const {
	return ofToFloat(getValue());
}


float ofxXmlPoco::getFloatValue(const string & path) const {
	return getValue <float>(path, 0.0);
}


bool ofxXmlPoco::getBoolValue() const {
	return ofToBool(getValue());
}


bool ofxXmlPoco::getBoolValue(const string & path) const {
	return getValue <bool>(path, false);
}


int64_t ofxXmlPoco::getInt64Value() const {
	return ofToInt64(getValue());
}


int64_t ofxXmlPoco::getInt64Value(const string & path) const {
	return getValue <int64_t>(path, 0);
}


bool ofxXmlPoco::reset(){
    if(element){
        element = document->documentElement();
        return true;
    }
    ofLogWarning("ofxXmlPoco") << "reset(): no element set yet";
    return false;
}

bool ofxXmlPoco::setToChild(unsigned long index){
    if(!element){
        if((Poco::XML::Element*) document->documentElement()->firstChild()){
            element = (Poco::XML::Element*) document->documentElement()->firstChild();
        }else{
            ofLogWarning("ofxXmlPoco") << "setToChild(): no element created yet";
            return false;
        }
    }
    
	unsigned long numberOfChildren = 0;
    Poco::XML::NodeList *list = element->childNodes();

    for(unsigned long i=0; i < list->length() && numberOfChildren < index + 1; i++){
        if(list->item(i) && list->item(i)->nodeType() == Poco::XML::Node::ELEMENT_NODE){
            if(numberOfChildren == index){
                element = (Poco::XML::Element*) list->item(i);
                return true;
            }
            numberOfChildren++;
        }
    }

    return false;
}

bool ofxXmlPoco::setToParent(){
    if(element->parentNode()){
        element = (Poco::XML::Element*) element->parentNode();
    }else{
        ofLogWarning("ofxXmlPoco") << "setToParent(): current element has no parent";
        return false;
    }
    return true;
}

bool ofxXmlPoco::setToParent(int numLevelsUp){
    if(element){
        int i = 0;
        while( i < numLevelsUp ){
            if(element->parentNode()){
                element = (Poco::XML::Element*) element->parentNode();
            }else{
                ofLogWarning("ofxXmlPoco") << "setToParent(): too many parents: " << numLevelsUp;
                return false;
            }
            i++;
        }

        return true;
    }

    ofLogWarning("ofxXmlPoco") << "setToParent(): no element set yet";
    return false;    
}

bool ofxXmlPoco::setToSibling(){
    Poco::XML::Element *node;
    if(element){
        node = (Poco::XML::Element*) element->nextSibling();
    }else{
        ofLogWarning("ofxXmlPoco") << "setToSibling() << no element set yet";
        return false;
    }

	/* If we get NULL for node, then we do not have a sibling.
	   We can only savely check the type on a non-Null node (thus
	   avoiding NULL-pointer dereferences). Empty space is treated
	   as a text node and we do not want that. We are also not
	   interessted in comments. If we find a non-TEXT_NODE or
	   non-COMMENT_NODE, we do not look further for a sibling. */
	while(NULL != node){
		if((node->nodeType() == Poco::XML::Node::TEXT_NODE)
			|| (node->nodeType() == Poco::XML::Node::COMMENT_NODE)){
			node = (Poco::XML::Element*) node->nextSibling();
		}else{
			break;
		}
	}
	// make sure we actually got a sibling
	if(NULL == node){
		return false;
	}

    // we're cool now
    element = node;
    return true;
}

bool ofxXmlPoco::setToPrevSibling(){
    Poco::XML::Element *node;
    if(element){
        node = (Poco::XML::Element*) element->previousSibling();
    }else{
        ofLogWarning("ofxXmlPoco") << "setToPrevSibling(): no element set yet";
        return false;
    }

    // empty space in the XML doc is treated as text nodes. blerg.
    while(node && node->nodeType() == Poco::XML::Node::TEXT_NODE){
        node = (Poco::XML::Element*) node->previousSibling();
    }

    if(!node || node->nodeType() == Poco::XML::Node::TEXT_NODE){
        return false;
    }

    element = node;
    return true;
}

bool ofxXmlPoco::setValue(const string& path, const string& value){
    Poco::XML::Element *e;
    if(element){
        e = (Poco::XML::Element*) element->getNodeByPath(path);
    }else{
        ofLogWarning("ofxXmlPoco") << "setValue(): no element set yet";
        return false;
    }

    if(!e){
        ofLogWarning("ofxXmlPoco") <<  "setValue(): path \"" + path + "\" doesn't exist";
        return false;
    }
    
    if(!e->firstChild()){
    	Poco::XML::Text *node = getPocoDocument()->createTextNode(ofToString(value));
    	e->appendChild(node);
    	node->release();
        return true;
    }

    if(e->firstChild()->nodeType() == Poco::XML::Node::TEXT_NODE){
        Poco::XML::Text *node = getPocoDocument()->createTextNode(ofToString(value));
        e->replaceChild( (Poco::XML::Node*) node, e->firstChild()); // swap out
        node->release();
        return true;
    }else{
    	return false;
    }
}

string ofxXmlPoco::getAttribute(const string& path) const{
    Poco::XML::Node *e;
    if(element){
        if(path.find("[@") == string::npos){
            // we need to create a proper path
            string attributePath = "[@" + path + "]";
            e = element->getNodeByPath(attributePath);
        }else{
            e = element->getNodeByPath(path);
        }
    }else{
        ofLogWarning("ofxXmlPoco") << "getAttribute(): no element set yet";
        return "";
    }

    if(e){
        return e->getNodeValue(); // this will be the value of the attribute
    }
    return "";
}

bool ofxXmlPoco::removeAttribute(const string& path){
    string attributeName, pathToAttribute;

    Poco::XML::Element *e;
    if(element){
        bool hasPath = false;
        // you can pass either /node[@attr] or just attr
        if(path.find("[@") != string::npos){
            int attrBegin = path.find("[@");
            int start = attrBegin + 2;
            int end = path.find("]", start);
            attributeName = path.substr( start, end - start );
            pathToAttribute = path.substr(0, attrBegin);
            hasPath = true;
        }else{
            attributeName = path;
        }

        if(hasPath){
            e = (Poco::XML::Element*) element->getNodeByPath(pathToAttribute);
        }else{
            e = element;
        }

    }else{
        ofLogWarning("ofxXmlPoco") << "clearAttributes(): no element set yet";
        return false;
    }

    if(e){
        Poco::XML::NamedNodeMap *map = e->attributes();

		for(unsigned long i = 0; i < map->length(); i++){
            if(map->item(i)->nodeName() == attributeName){
                e->removeAttribute(map->item(i)->nodeName());
            }
        }

        map->release();
        return true;
    }
    return false;
}

bool ofxXmlPoco::removeAttributes(const string& path){
    Poco::XML::Element *e;
    if(element){
        if(path.find("[@") == string::npos){
            // we need to create a proper path
            string attributePath = "[@" + path + "]";
            e = (Poco::XML::Element*) element->getNodeByPath(attributePath);
        }else{
            e = (Poco::XML::Element*) element->getNodeByPath(path);
        }
    }else{
        ofLogWarning("ofxXmlPoco") << "clearAttributes(): no element set yet";
        return false;
    }

    if(e){
        Poco::XML::NamedNodeMap *map = e->attributes();

        for(unsigned long i = 0; i < map->length(); i++){
            e->removeAttribute(map->item(i)->nodeName());
        }

        map->release();
        return true;
    }
    return false;
}

bool ofxXmlPoco::removeAttributes(){

    if(element){
        Poco::XML::NamedNodeMap *map = element->attributes();

        for(unsigned long i = 0; i < map->length(); i++){
            element->removeAttribute(map->item(i)->nodeName());
        }

        map->release();
        return true;
    }
    ofLogWarning("ofxXmlPoco") << "clearAttributes(): no element set yet";
    return false;
}

bool ofxXmlPoco::removeContents(){
    if(element && element->hasChildNodes()){

		Poco::XML::Node* swap;
		Poco::XML::Node* n = element->firstChild();
		while(n->nextSibling() != nullptr){
			swap = n->nextSibling();
			element->removeChild(n);
			n = swap;
		}

        return true;
    }
    return false;
}

bool ofxXmlPoco::removeContents(const string& path){    
    Poco::XML::Element *e;
    if(element){
        e = (Poco::XML::Element*) element->getNodeByPath(path);
    }else{
        ofLogWarning("ofxXmlPoco") << "clearContents(): no element set yet";
        return false;
    }

    if(e){
        Poco::XML::NodeList *list = e->childNodes();
        for(unsigned long i = 0; i < list->length(); i++) {
            element->removeChild(list->item(i));
        }
        list->release();
        return true;
    }
    return false;
}


void ofxXmlPoco::clear(){
	releaseAll();
	document = new Poco::XML::Document(); // we create this so that they can be merged later
	element = document->documentElement();
}


void ofxXmlPoco::releaseAll(){
	if(document){
		document->release();
		document = NULL;
	}
	element = NULL;
}


bool ofxXmlPoco::remove(const string & path){ // works for both attributes and tags
	Poco::XML::Node * node;
	if(element){
		node = element->getNodeByPath(path);
	}else{
		ofLogWarning("ofxXmlPoco") << "remove(): no element set yet";
		return false;
	}

	if(node){
		Poco::XML::Node * n = node->parentNode()->removeChild(node);
		n->release();
		return true;
	}
	return false;
}


void ofxXmlPoco::remove(){
	Poco::XML::Node * parent = element->parentNode();
	if(parent){
		parent->removeChild(element);
		element->release();
		element = (Poco::XML::Element *)parent;
	}else{
		clear();
	}
}


bool ofxXmlPoco::exists(const string & path) const{ // works for both attributes and tags
	Poco::XML::Node * node;
	if(element){
		node = element->getNodeByPath(path);
	}else{
		return false;
	}

	if(node){
		return true;
	}
	return false;
}

map<string, string> ofxXmlPoco::getAttributes() const{ // works for both attributes and tags
    map<string, string> attrMap;

    if(element){
        Poco::AutoPtr<Poco::XML::NamedNodeMap> attr = element->attributes();
        for(unsigned long i = 0; i < attr->length(); i++){
            attrMap[attr->item(i)->nodeName()] = attr->item(i)->nodeValue();
        }
    }else{
        ofLogWarning("ofxXmlPoco") << "getAttribute(): no element set";
    }

    return attrMap;
}


bool ofxXmlPoco::setAttribute(const string& path, const string& value){

    string attributeName, pathToAttribute;
    bool hasPath = false;

    // you can pass either /node[@attr] or just attr
    if(path.find("[@") != string::npos){
        size_t attrBegin = path.find("[@");
        size_t start = attrBegin + 2;
        size_t end = path.find("]", start);
        attributeName = path.substr( start, end - start );
        pathToAttribute = path.substr(0, attrBegin);
        hasPath = true;
    }else{
        attributeName = path;
    }

    // we don't have a path to resolve
    Poco::AutoPtr<Poco::XML::Attr> attr = getPocoDocument()->createAttribute(attributeName);
    attr->setValue(value);
    
    if(!hasPath){
        Poco::AutoPtr<Poco::XML::NamedNodeMap> map = element->attributes();
        map->setNamedItem(attr);
        return true; // and we're done
    }

    // we have a path to resolve
    Poco::XML::Element* curElement = getPocoElement(pathToAttribute);

    if(!curElement){ // if it doesn't exist
        vector<string> tokens;

        if(path.find('/') != string::npos){
            tokens = tokenize(pathToAttribute, "/");
        }

        // is this a tokenized tag?
        if(tokens.size() > 1){
            // don't 'push' down into the new nodes
            curElement = element;

            // find the last existing tag
            size_t lastExistingTag = 0;

            // can't use reverse_iterator b/c accumulate doesn't like it
            for(vector<string>::iterator it = tokens.end(); it != tokens.begin(); it--){
                string empty = "";
                string concat = accumulate(tokens.begin(), it, std::string());
                Poco::XML::Element* testElement = getPocoElement(concat);
                if(testElement){
                    lastExistingTag++;
                    curElement = testElement;
                    break;
                }
            }

            // create all the tags that don't exist
			for(size_t i = lastExistingTag; i < tokens.size(); i++){
                Poco::XML::Element *newElement = getPocoDocument()->createElement(tokens.at(i));
                curElement->appendChild(newElement);
                curElement = newElement;
            }

            curElement->setAttribute(attributeName, value);
            return true;
        }else{
            Poco::XML::Element* testElement = getPocoElement(pathToAttribute);
            if(testElement){
                curElement = testElement;
            }else{
                Poco::XML::Element *newElement = getPocoDocument()->createElement(pathToAttribute);
                curElement->appendChild(newElement);
                curElement = newElement;
            }

            curElement->setAttribute(attributeName, value);
            return true;
        }
    }
    return false;
}


bool ofxXmlPoco::loadFromBuffer(const string & buffer){
	Poco::XML::DOMParser parser;

	// release and null out if we already have a document
	if(document){
		document->release();
	}

	try{
		document = parser.parseString(buffer);
		element = (Poco::XML::Element *)document->firstChild();
		document->normalize();
		return true;
	}
	catch(const Poco::XML::SAXException & e){
		ofLogError("ofxXmlPoco") << "parse error: " << e.message();
		document = new Poco::XML::Document;
		element = document->documentElement();
		return false;
	}
	catch(const exception & e){
		short msg = atoi(e.what());
		ofLogError("ofxXmlPoco") << "parse error: " << DOMErrorMessage(msg);
		document = new Poco::XML::Document;
		element = document->documentElement();
		return false;
	}
}


string ofxXmlPoco::getName() const {
	if(element){
		return element->nodeName();
	}
	return "";
}

bool ofxXmlPoco::setTo(const string& path){
    if(!element){
        if(document->documentElement()) {
            element = document->documentElement();
        }else{
            ofLogWarning("ofxXmlPoco") << "setTo(): empty document";
            return false;
        }
    }


    // one case: we're at the root, but we don't know it yet:
    if(element == document->documentElement() && element->nodeName() == path ){
        return true;
    }

    //ofLogNotice("ofxXmlPoco") << path << " " << path.find("../");

    // another: let's go up a little
    if(path.find("../") != string::npos){
 
        Poco::XML::Element* prev = element;
        Poco::XML::Element* parent = nullptr;
		size_t count = 0;
        size_t offset;
        for (offset = path.find("../");
             offset != std::string::npos;
             offset = path.find("../", offset + 3)){

            if(count == 0){
                parent = (Poco::XML::Element*) element->parentNode();
            }else{
                parent = (Poco::XML::Element*) parent->parentNode();
            }
            ++count;
        }

        //ofLogNotice("ofxXmlPoco") << (count * 3) << " " << path.size();

        if( (count * 3) > path.size() - 1 ){
            element = parent;
            return true;
        }else if (parent){
            string remainingPath = path.substr((count * 3), path.size() - (count * 3));
            element = (Poco::XML::Element*) parent->getNodeByPath(remainingPath);

             if(!element){
                 element = prev;
                 ofLogWarning("ofxXmlPoco") << "setCurrentElement(): passed invalid path \"" << remainingPath << "\"";
                 return false;
             }
        }else{
            ofLogWarning("ofxXmlPoco") << "setCurrentElement(): parent is nullptr.";
            return false;
        }
    }else if(path.find("//") != string::npos){
        // another: we're looking all over
        Poco::XML::Element* prev = element;
        element = (Poco::XML::Element*) document->getNodeByPath(path);
        if(!element){
            element = prev;
            ofLogWarning("ofxXmlPoco") << "setCurrentElement(): passed invalid path \"" << path << "\"";
            return false;
        }
    }else{
        // another: we're actually looking down into the thing :)
        Poco::XML::Element* prev = element;
        element = (Poco::XML::Element*) element->getNodeByPath(path);
        if(!element){
            element = prev;
            ofLogWarning("ofxXmlPoco") << "setCurrentElement(): passed invalid path \"" << path << "\"";
            return false;
        }
    }
    return true;
}


const Poco::XML::Element * ofxXmlPoco::getPocoElement() const {
	return element;
}


Poco::XML::Element * ofxXmlPoco::getPocoElement(){
	return element;
}

Poco::XML::Element* ofxXmlPoco::getPocoElement(const string& path){
    string copy = path;
    // does it have an attribute? just in case
    std::size_t ind = copy.find("[@");
    if(ind != string::npos){
        copy = path.substr(0, ind);
    }

    if(element){
        return (Poco::XML::Element*) element->getNodeByPath(copy);
    }else{
        ofLogWarning("ofxXmlPoco") << "getPocoElement(): no element to get yet ";
        return nullptr;
    }
}

const Poco::XML::Element* ofxXmlPoco::getPocoElement(const string& path) const{
    string copy = path;
    // does it have an attribute? just in case
	std::size_t ind = copy.find("[@");
    if(ind != string::npos){
        copy = path.substr(0, ind);
    }

    if(element){
        return (Poco::XML::Element*) element->getNodeByPath(copy);
    }else{
        ofLogWarning("ofxXmlPoco") << "getPocoElement(): no element to get yet ";
        return nullptr;
    }
}


Poco::XML::Document * ofxXmlPoco::getPocoDocument(){
	return document;
}


const Poco::XML::Document * ofxXmlPoco::getPocoDocument() const {
	return document;
}


string ofxXmlPoco::DOMErrorMessage(short msg){
	switch(msg){
	 case 1:
		 return "INDEX_SIZE_ERR";
		 break;   /// index or size is negative or greater than allowed value
	 case 2:
		 return "DOMSTRING_SIZE_ERR";             /// the specified range of text does not fit into a DOMString (not used)
		 break;
	 case 3:
		 return "HIERARCHY_REQUEST_ERR";          /// a node is inserted somewhere it doesn't belong
		 break;
	 case 4:
		 return "WRONG_DOCUMENT_ERR";             /// a node is used in a different document than the one that created it
		 break;
	 case 5:
		 return "INVALID_CHARACTER_ERR";          /// an invalid character is specified (not used)
		 break;
	 case 6:
		 return "NO_DATA_ALLOWED_ERR";            /// data is specified for a node which does not support data
		 break;
	 case 7:
		 return "NO_MODIFICATION_ALLOWED_ERR";    /// an attempt is made to modify an object where modifications are not allowed
		 break;
	 case 8:
		 return "NOT_FOUND_ERR";                  /// an attempt was made to reference a node in a context where it does not exist
		 break;
	 case 9:
		 return "NOT_SUPPORTED_ERR";              /// the implementation does not support the type of object requested
		 break;
	 case 10:
		 return "INUSE_ATTRIBUTE_ERR";            /// an attempt is made to add an attribute that is already in use elsewhere
		 break;
	 case 11:
		 return "INVALID_STATE_ERR";              /// a parameter or an operation is not supported by the underlying object
		 break;
	 case 12:
		 return "SYNTAX_ERR";                     /// an invalid or illegal string is specified
		 break;
	 case 13:
		 return "INVALID_MODIFICATION_ERR";       /// an attempt is made to modify the type of the underlying object
		 break;
	 case 14:
		 return "NAMESPACE_ERR";                  /// an attempt is made to create or change an object in a way which is incorrect with regard to namespaces
		 break;
	 case 15:
		 return "INVALID_ACCESS_ERR";             /// an attempt is made to use an object that is not or is no longer usable
		 break;
	}

	return "DOM ERROR";
}


void ofSerialize(ofxXmlPoco & xml, const ofAbstractParameter & parameter){
	if(!parameter.isSerializable()){
		return;
	}
	string name = parameter.getEscapedName();
	if(name == ""){
		name = "UnknownName";
	}
	if(parameter.type() == typeid(ofParameterGroup).name()){
		const ofParameterGroup & group = static_cast <const ofParameterGroup &>(parameter);
		if(!xml.exists(name)){
			xml.addChild(name);
			ofLogVerbose("ofxXmlPoco") << "creating group " << name;
		}
		xml.setTo(name);
		ofLogVerbose("ofxXmlPoco") << "group " << name;
		for(auto & p: group){
			ofSerialize(xml, *p);
		}
		ofLogVerbose("ofxXmlPoco") << "end group " << name;
		xml.setToParent();
	}else{
		string value = parameter.toString();
		if(!xml.exists(name)){
			xml.addChild(name);
			ofLogVerbose("ofxXmlPoco") << "creating tag " << name;
		}
		ofLogVerbose("ofxXmlPoco") << "setting tag " << name << ": " << value;
		xml.setValue(name, value);
	}
}


void ofDeserialize(const ofxXmlPoco & xml, ofAbstractParameter & parameter){
	if(!parameter.isSerializable()){
		return;
	}
	string name = parameter.getEscapedName();
	if(parameter.type() == typeid(ofParameterGroup).name()){
		ofParameterGroup & group = static_cast <ofParameterGroup &>(parameter);
		if(const_cast<ofxXmlPoco&>(xml).setTo(name)){
			for(auto & p: group){
				ofDeserialize(xml, *p);
			}
			const_cast<ofxXmlPoco&>(xml).setToParent();
		}
	}else{
		if(xml.exists(name)){
			if(parameter.type() == typeid(ofParameter <int> ).name()){
				parameter.cast <int>() = xml.getIntValue(name);
			}else if(parameter.type() == typeid(ofParameter <float> ).name()){
				parameter.cast <float>() = xml.getFloatValue(name);
			}else if(parameter.type() == typeid(ofParameter <bool> ).name()){
				parameter.cast <bool>() = xml.getBoolValue(name);
			}else if(parameter.type() == typeid(ofParameter <int64_t> ).name()){
				parameter.cast <int64_t>() = xml.getInt64Value(name);
			}else if(parameter.type() == typeid(ofParameter <string> ).name()){
				parameter.cast <string>() = xml.getValue(name);
			}else{
				parameter.fromString(xml.getValue(name));
			}
		}
	}

}
