#include "acl_stdafx.hpp"
#include "acl_cpp/stdlib/snprintf.hpp"
#include "acl_cpp/stdlib/string.hpp"
#include "acl_cpp/stdlib/log.hpp"
#include "acl_cpp/stdlib/xml1.hpp"

namespace acl {

xml1_attr::xml1_attr(xml_node* node, ACL_XML_ATTR* attr)
	: xml_attr(node)
	, attr_(attr)
{
	acl_assert(attr_);
}

const char* xml1_attr::get_name(void) const
{
	if (attr_->name)
		return acl_vstring_str(attr_->name);
	else
		return "";
}

const char* xml1_attr::get_value(void) const
{
	if (attr_->value)
		return acl_vstring_str(attr_->value);
	else
		return "";
}

//////////////////////////////////////////////////////////////////////////

xml1_node::xml1_node(xml* xml_ptr, ACL_XML_NODE* node)
	: xml_node(xml_ptr)
	, node_(node)
	, child_iter_(NULL)
	, attr_iter_(NULL)
	, parent_(NULL)
	, parent_internal_(NULL)
{
}

xml1_node::~xml1_node(void)
{
	delete parent_internal_;
	if (child_iter_)
		acl_myfree(child_iter_);
	if (attr_iter_)
		acl_myfree(attr_iter_);
}

ACL_XML_NODE* xml1_node::get_xml_node(void) const
{
	return node_;
}

const char* xml1_node::tag_name(void) const
{
	if (node_->ltag && ACL_VSTRING_LEN(node_->ltag) > 0)
		return (acl_vstring_str(node_->ltag));
	else
		return "";
}

const char* xml1_node::text(void) const
{
	if (node_->text && ACL_VSTRING_LEN(node_->text) > 0)
		return acl_vstring_str(node_->text);
	else
		return "";
}

const char* xml1_node::id(void) const
{
	if (node_->id && ACL_VSTRING_LEN(node_->id) > 0)
		return acl_vstring_str(node_->id);
	else
		return "";
}

const char* xml1_node::attr_value(const char* name) const
{
	return acl_xml_getElementAttrVal(node_, name);
}

const xml_attr* xml1_node::first_attr(void) const
{
	ACL_ARRAY* a = node_->attr_list;
	if (a == NULL)
		return NULL;

	if (attr_iter_ == NULL)
		const_cast<xml1_node*>(this)->attr_iter_ =
			(ACL_ITER*) acl_mymalloc(sizeof(ACL_ITER));

	ACL_XML_ATTR* attr = (ACL_XML_ATTR*) a->iter_head(attr_iter_, a);
	if (attr == NULL)
		return NULL;

	xml1_attr* xa = NEW xml1_attr(const_cast<xml1_node*>(this), attr);
	const_cast<xml1_node*>(this)->attrs_tmp_.push_back(xa);
	return xa;
}

const xml_attr* xml1_node::next_attr(void) const
{
	ACL_ARRAY* a = node_->attr_list;
	if (a == NULL)
		return NULL;

	acl_assert(attr_iter_);

	ACL_XML_ATTR* attr = (ACL_XML_ATTR*) a->iter_next(attr_iter_, a);
	if (attr == NULL)
		return NULL;

	xml1_attr* xa = NEW xml1_attr(const_cast<xml1_node*>(this), attr);
	const_cast<xml1_node*>(this)->attrs_tmp_.push_back(xa);
	return xa;
}

xml_node& xml1_node::add_attr(const char* name, const char* value)
{
	acl_xml_node_add_attr(node_, name, value);
	return *this;
}

xml_node& xml1_node::set_text(const char* str)
{
	acl_xml_node_set_text(node_, str);
	return *this;
}

xml_node& xml1_node::add_child(xml_node* child, bool return_child /* = false */)
{
	ACL_XML_NODE* node = ((xml1_node*) child)->get_xml_node();
	acl_xml_node_add_child(node_, node);
	child->set_parent(this);

	if (return_child)
		return *child;
	return *this;
}

int xml1_node::detach(void)
{
	return acl_xml_node_delete(node_);
}

xml_node& xml1_node::set_parent(xml_node* parent)
{
	parent_ = parent;
	return *this;
}

xml_node& xml1_node::get_parent() const
{
	if (parent_)
		return *parent_;
	else if (node_->parent == node_->xml->root)
		return xml_->get_root();
	else if (node_->parent == NULL)  // xxx: can this happen?
		return xml_->get_root();

	xml1_node* node = NEW xml1_node(xml_, node_->parent);
	const_cast<xml1_node*>(this)->parent_internal_ = node;
	const_cast<xml1_node*>(this)->parent_ = node;

	return *node;
}

xml_node* xml1_node::first_child(void)
{
	if (child_iter_ == NULL)
		child_iter_ = (ACL_ITER*) acl_mymalloc(sizeof(ACL_ITER));

	ACL_XML_NODE* node = node_->iter_head(child_iter_, node_);
	if (node == NULL)
		return NULL;

	xml1_node* n = NEW xml1_node(xml_, node);
	nodes_tmp_.push_back(n);
	return n;
}

xml_node* xml1_node::next_child(void)
{
	acl_assert(child_iter_);

	ACL_XML_NODE* node = node_->iter_next(child_iter_, node_);
	if (node == NULL)
		return NULL;

	xml1_node* n = NEW xml1_node(xml_, node);
	nodes_tmp_.push_back(n);

	return n;
}

int xml1_node::depth(void) const
{
	return node_->depth;
}

bool xml1_node::is_root(void) const
{
	xml1_node& node = (xml1_node&) ((xml1*) xml_)->get_root();
	ACL_XML_NODE* root = node.get_xml_node();
	return root == node_;
}

int xml1_node::children_count(void) const
{
	return acl_ring_size(&node_->children);
}

//////////////////////////////////////////////////////////////////////

xml1::xml1(const char* data /* = NULL */)
{
	iter_ = NULL;
	root_ = NULL;

	xml_ = acl_xml_alloc();
	if (data && *data)
		update(data);
}

xml1::~xml1(void)
{
	if (iter_)
		acl_myfree(iter_);
	delete root_;
	acl_xml_free(xml_);
}

xml& xml1::ignore_slash(bool on)
{
	acl_xml_slash(xml_, on ? 1 : 0);
	return *this;
}

xml& xml1::xml_decode(bool on)
{
	acl_xml_decode_enable(xml_, on ? 1 : 0);
	return *this;
}

void xml1::update(const char* data)
{
	acl_xml_update(xml_, data);
}

const std::vector<xml_node*>& xml1::getElementsByTagName(const char* tag) const
{
	const_cast<xml1*>(this)->clear();

	ACL_ARRAY* a = acl_xml_getElementsByTagName(xml_, tag);
	if (a == NULL)
		return elements_;

	ACL_ITER iter;
	acl_foreach(iter, a)
	{
		ACL_XML_NODE *tmp = (ACL_XML_NODE*) iter.data;
		xml1_node* node = NEW xml1_node(const_cast<xml1*>(this), tmp);
		const_cast<xml1*>(this)->elements_.push_back(node);
	}

	acl_xml_free_array(a);
	return elements_;
}

xml_node* xml1::getFirstElementByTag(const char* tag) const
{
	ACL_XML_NODE* node = acl_xml_getFirstElementByTagName(xml_, tag);
	if (node == NULL)
		return NULL;

	xml1_node* n = NEW xml1_node(const_cast<xml1*>(this), node);
	const_cast<xml1*>(this)->nodes_tmp_.push_back(n);
	return n;
}

const std::vector<xml_node*>& xml1::getElementsByTags(const char* tags) const
{
	const_cast<xml1*>(this)->clear();

	ACL_ARRAY* a = acl_xml_getElementsByTags(xml_, tags);
	if (a == NULL)
		return elements_;

	ACL_ITER iter;
	acl_foreach(iter, a)
	{
		ACL_XML_NODE *tmp = (ACL_XML_NODE*) iter.data;
		xml1_node* node = NEW xml1_node(const_cast<xml1*>(this), tmp);
		const_cast<xml1*>(this)->elements_.push_back(node);
	}

	acl_xml_free_array(a);
	return elements_;
}

xml_node* xml1::getFirstElementByTags(const char* tags) const
{
	ACL_ARRAY* a = acl_xml_getElementsByTags(xml_, tags);
	if (a == NULL)
		return NULL;

	ACL_XML_NODE* node = (ACL_XML_NODE*) acl_array_index(a, 0);
	acl_assert(node);

	xml1_node* n = NEW xml1_node(const_cast<xml1*>(this), node);
	const_cast<xml1*>(this)->nodes_tmp_.push_back(n);

	acl_xml_free_array(a);
	return n;
}

const std::vector<xml_node*>& xml1::getElementsByName(const char* value) const
{
	const_cast<xml1*>(this)->clear();
	ACL_ARRAY* a = acl_xml_getElementsByName(xml_, value);
	if (a == NULL)
		return elements_;

	ACL_ITER iter;
	acl_foreach(iter, a)
	{
		ACL_XML_NODE *tmp = (ACL_XML_NODE*) iter.data;
		xml1_node* node = NEW xml1_node(const_cast<xml1*>(this), tmp);
		const_cast<xml1*>(this)->elements_.push_back(node);
	}

	acl_xml_free_array(a);
	return elements_;
}

const std::vector<xml_node*>& xml1::getElementsByAttr(
	const char *name, const char *value) const
{
	const_cast<xml1*>(this)->clear();
	ACL_ARRAY *a = acl_xml_getElementsByAttr(xml_, name, value);
	if (a == NULL)
		return elements_;

	ACL_ITER iter;
	acl_foreach(iter, a)
	{
		ACL_XML_NODE *tmp = (ACL_XML_NODE*) iter.data;
		xml1_node* node = NEW xml1_node(const_cast<xml1*>(this), tmp);
		const_cast<xml1*>(this)->elements_.push_back(node);
	}

	acl_xml_free_array(a);
	return elements_;
}

xml_node* xml1::getElementById(const char* id) const
{
	ACL_XML_NODE* node = acl_xml_getElementById(xml_, id);
	if (node == NULL)
		return NULL;

	xml1_node* n = NEW xml1_node(const_cast<xml1*>(this), node);
	const_cast<xml1*>(this)->nodes_tmp_.push_back(n);
	return n;
}

const acl::string& xml1::getText()
{
#define	STR(x)		acl_vstring_str((x))
#define	EQ(x, y)	!strcasecmp((x), (y))

	if (m_pTokenTree == NULL)
	{
		static const char* s = "&nbsp; &lt; &gt; &amp; &quot;"
			" &NBSP; &LT; &GT; &AMP; &QUOT;";
		m_pTokenTree = acl_token_tree_create2(s, " ");
	}

	string* pBuf = NEW string(1024);
	ACL_ITER iter;
	acl_foreach(iter, xml_)
	{
		ACL_XML_NODE *node = (ACL_XML_NODE*) iter.data;

		if ((node->flag & ACL_XML_F_META))
			continue;
		if (EQ(STR(node->ltag), "style")
			|| EQ(STR(node->ltag), "meta")
			|| EQ(STR(node->ltag), "head")
			|| EQ(STR(node->ltag), "title")
			|| EQ(STR(node->ltag), "script"))
		{
			continue;
		}

		pBuf->append(STR(node->text));
	}

	if (buf_ == NULL)
		buf_ = NEW string(pBuf->length());
	else
		buf_->clear();

	const ACL_TOKEN *token;
	const char* ptr = pBuf->c_str(), *pLast;
	ACL_VSTRING* name = acl_vstring_alloc(8);
	while (*ptr)
	{
		pLast = ptr;
		ACL_TOKEN_TREE_MATCH(m_pTokenTree, ptr, NULL, NULL, token);
		if (token == NULL)
		{
			buf_->append(pLast, ptr - pLast);
			continue;
		}

		acl_token_name(token, name);

		if (EQ(STR(name), "&nbsp;"))
		{
			if (ptr - pLast - 6 > 0)
				buf_->append(pLast, ptr - pLast - 6);
			*buf_ += ' ';
		}
		else if (EQ(STR(name), "&amp;"))
		{
			if (ptr - pLast - 5 > 0)
				buf_->append(pLast, ptr - pLast - 5);
			*buf_ += '&';
		}
		else if (EQ(STR(name), "&lt;"))
		{
			if (ptr - pLast - 4 > 0)
				buf_->append(pLast, ptr - pLast - 4);
			*buf_ += '<';
		}
		else if (EQ(STR(name), "&gt;"))
		{
			if (ptr - pLast - 4 > 0)
				buf_->append(pLast, ptr - pLast - 4);
			*buf_ += '>';
		}
		else if (EQ(STR(name), "&quot;"))
		{
			if (ptr - pLast - 6 > 0)
				buf_->append(pLast, ptr - pLast - 6);
			*buf_ += '"';
		}
	}

	acl_vstring_free(name);
	delete pBuf;

	return *buf_;
}

xml_node& xml1::create_node(const char* tag, const char* text /* = NULL */)
{
	ACL_XML_NODE* node = acl_xml_create_node(xml_, tag, text);
	xml1_node* n = NEW xml1_node(this, node);
	nodes_tmp_.push_back(n);
	return *n;
}

xml_node& xml1::get_root(void)
{
	if (root_)
		return *root_;
	root_ = NEW xml1_node(this, xml_->root);
	return *root_;
}

xml_node* xml1::first_node(void)
{
	if (iter_ == NULL)
		iter_ = (ACL_ITER*) acl_mymalloc(sizeof(ACL_ITER));

	ACL_XML_NODE* node = xml_->iter_head(iter_, xml_);
	if (node == NULL)
		return NULL;

	xml1_node* n = NEW xml1_node(this, node);
	nodes_tmp_.push_back(n);
	return n;
}

xml_node* xml1::next_node(void)
{
	acl_assert(iter_);

	ACL_XML_NODE* node = xml_->iter_next(iter_, xml_);
	if (node == NULL)
		return NULL;

	xml1_node* n = NEW xml1_node(this, node);
	nodes_tmp_.push_back(n);
	return n;
}

void xml1::build_xml(string& out) const
{
	ACL_VSTRING* buf = out.vstring();
	(void) acl_xml_build(xml_, buf);
}

const char* xml1::to_string(size_t* len /* = NULL */) const
{
	if (buf_ == NULL)
		const_cast<xml1*>(this)->buf_ = NEW string();
	else
		const_cast<xml1*>(this)->buf_->clear();

	build_xml(*buf_);
	if (len)
		*len = buf_->size();
	return buf_->c_str();
}

void xml1::reset(void)
{
	clear();
	delete root_;
	root_ = NULL;
	acl_xml_reset(xml_);
	//dummyRootAdded_ = false;
}

} // namespace acl
