// $Id: CXUActionBase.cc,v 1.7 2003/05/10 06:46:51 ta_muto Exp $

#include <xupdate/CXUActionBase.h>
#include <CXmlLibs.h>
#include <CWaxLibs.h>

xmlChar* wax::CXUActionBase::getSelect(xmlNodePtr cur)
{
	xmlChar* select = xmlGetProp(cur, (const xmlChar*) "select");
	if(select == NULL) {
		IError::message(ERR_ATTRIBUTE, "%s::select", cur->name);
		return NULL;
	}
	return select;
}

xmlDocPtr wax::CXUActionBase::parse(CEnvironment* runenv, xmlNodePtr cur, CCollection& vars, xmlChar*& connect)
{
	bool hasError = false;
	xmlNsPtr ns = xmlSearchNsByHref(runenv->getDocPtr(), cur, CEnvironment::WAX);
	xmlNsPtr nsXUpdate = xmlSearchNsByHref(runenv->getDocPtr(), cur, CEnvironment::XUPDATE);
	xmlDocPtr doc = xmlNewDoc((const xmlChar*)"1.0");
	xmlNodePtr node = parseInternal(runenv, cur->children, ns, nsXUpdate, NULL, vars, connect, hasError);
	if(hasError == true) {
		xmlFreeDoc(doc);
		return NULL;
	}
	if(node == NULL) {
		xmlFreeDoc(doc);
		// empty document.
		return NULL;
	}
	xmlDocSetRootElement(doc, node);
	return doc;
}

xmlNodePtr wax::CXUActionBase::parseInternal(CEnvironment* runenv, xmlNodePtr cur, xmlNsPtr ns, xmlNsPtr nsXUpdate, xmlNodePtr parent, CCollection& vars, xmlChar*& connect, bool& hasError)
{
	if(hasError == true)	return NULL;

	xmlNodePtr retItem = NULL;
	while(cur != NULL) {
		if(cur->ns == ns) {
			if(!xmlStrcmp(cur->name, (const xmlChar*) "connect")) {
				CXmlStr href(xmlGetProp(cur, (const xmlChar*) "href"));
				if(href.value() == NULL) {
					IError::message(ERR_ATTRIBUTE, "connect::href");
					hasError = true;
					return retItem;
				}
				if(connect != NULL)	xmlFree(connect);
				connect = runenv->makePath(href.value());
			} else if(!xmlStrcmp(cur->name, (const xmlChar*) "instanceData")) {
//				return generateInstance();
				IError::message(ERR_NOTSUPPORTED, "wax:instanceData");
				hasError = true;
				return NULL;
			} else {
				xmlChar* content = NULL;
				if(CWaxLibs::parse(runenv, cur, content) == false) {
					hasError = true;
					return retItem;
				}
				if(content != NULL) {
					retItem = newTextNode(retItem, content, hasError);
				}
			}
		} else if(cur->ns == nsXUpdate) {
			if(!xmlStrcmp(cur->name, (const xmlChar*) "element")) {
				CXmlStr name(xmlGetProp(cur, (const xmlChar*) "name"));
				if(name.value() == NULL) {
					IError::message(ERR_ATTRIBUTE, "name of xupdate:element");
					hasError = true;
					return retItem;
				}
				xmlNodePtr node = NULL;
				retItem = newElementNode(retItem, NULL, name.value(), node, hasError);
				xmlNodePtr child = parseInternal(runenv, cur->children, ns, nsXUpdate, node, vars, connect, hasError);
				if(child != NULL) {
					xmlAddChildList(node, child);
				}
			} else if(!xmlStrcmp(cur->name, (const xmlChar*) "attribute")) {
				if(parent != NULL) {
					CXmlStr name(xmlGetProp(cur, (const xmlChar*) "name"));
					if(name.value() == NULL) {
						IError::message(ERR_ATTRIBUTE, "name of xupdate:attribute");
						hasError = true;
						return retItem;
					}
					xmlNodePtr child = parseInternal(runenv, cur->children, ns, nsXUpdate, NULL, vars, connect, hasError);
					xmlSetNsProp(parent, NULL, name.value(), xmlNodeGetContent(child));
				} else {
					IError::message(ERR_XUPATTR);
					hasError = true;
					return retItem;
				}
			} else if(!xmlStrcmp(cur->name, (const xmlChar*) "text")) {
				xmlNodePtr child = cur->children;
				while(child != NULL) {
					if(child->type == XML_TEXT_NODE) {
						retItem = newTextNode(retItem, child->content, hasError);
						if(hasError == true)	return retItem;
					}
					child = child->next;
				}
			} else if(!xmlStrcmp(cur->name, (const xmlChar*) "processing-instruction")) {
				CXmlStr name(xmlGetProp(cur, (const xmlChar*) "name"));
				if(name.value() == NULL) {
					IError::message(ERR_ATTRIBUTE, "name of xupdate:processing-instruction");
					hasError = true;
					return retItem;
				}
				xmlNodePtr node = xmlNewPI(name.value(), xmlNodeGetContent(cur));
				retItem = addSibling(retItem, node);
			} else if(!xmlStrcmp(cur->name, (const xmlChar*) "comment")) {
				xmlNodePtr node = xmlNewComment(xmlNodeGetContent(cur));
				retItem = addSibling(retItem, node);
			} else if(!xmlStrcmp(cur->name, (const xmlChar*) "value-of")) {
				CXmlStr select(xmlGetProp(cur, (const xmlChar*) "select"));
				if(select.value() == NULL) {
					IError::message(ERR_ATTRIBUTE, "value-of::select");
					hasError = true;
					return retItem;
				}
				CXmlStr value(CWaxLibs::evaluateXPath(connect, vars, select.value()));
				if(value.value() == NULL) {
					hasError = true;
					return retItem;
				}
				retItem = newTextNode(retItem, value.value(), hasError);
			}
		} else {
			switch(cur->type){
			case XML_ELEMENT_NODE:
				{
					xmlNodePtr node = NULL;
					retItem = newElementNode(retItem, cur->ns, cur->name, node, hasError);
					if(hasError == true)	return retItem;
					xmlAttrPtr attr = cur->properties;
					while(attr != NULL) {
						xmlSetNsProp(node, attr->ns, attr->name, attr->children->content);
						attr = attr->next;
					}
					xmlNodePtr child = parseInternal(runenv, cur->children, ns, nsXUpdate, node, vars, connect, hasError);
					if(child != NULL) {
						xmlAddChildList(node, child);
					}
				}
				break;
			case XML_TEXT_NODE:
				if(cur->content != NULL) {
					retItem = newTextNode(retItem, cur->content, hasError);
					if(hasError == true)	return retItem;
				}
				break;
			default:
				break;
			}
		}
		cur = cur->next;
	}
	return retItem;
}

xmlNodePtr wax::CXUActionBase::addSibling(xmlNodePtr base, xmlNodePtr node)
{
	if(base == NULL) {
		return node;
	}
	xmlAddSibling(base, node);
	return base;
}

xmlNodePtr wax::CXUActionBase::newElementNode(xmlNodePtr retItem, xmlNsPtr ns, const xmlChar* name, xmlNodePtr& node, bool& hasError)
{
	node = xmlNewNode(ns, name);
	if(node == NULL) {
		IError::message(ERR_OUTOFMEMORY, LOCATION);
		hasError = true;
		return retItem;
	}
	return addSibling(retItem, node);
}

xmlNodePtr wax::CXUActionBase::newTextNode(xmlNodePtr retItem, const xmlChar* content, bool& hasError)
{
	xmlNodePtr node = xmlNewText(content);
	if(node == NULL) {
		IError::message(ERR_OUTOFMEMORY, LOCATION);
		hasError = true;
		return retItem;
	}
	return addSibling(retItem, node);
}
