/*
 * This file is part of libdom.
 * Licensed under the MIT License,
 *                http://www.opensource.org/licenses/mit-license.php
 * Copyright 2009 Bo Yang <struggleyb.nku@gmail.com>
 */

#include <assert.h>
#include <stdlib.h>

#include <libwapcaplet/libwapcaplet.h>

#include "html/html_options_collection.h"

#include "core/node.h"
#include "core/element.h"
#include "core/string.h"
#include "utils/utils.h"

/*-----------------------------------------------------------------------*/
/* Constructor and destructor */

/**
 * Create a dom_html_options_collection
 *
 * \param doc   The document
 * \param root  The root element of the collection
 * \param ic    The callback function used to determin whether certain node
 *              beint32_ts to the collection
 * \param col   The result collection object
 * \return DOM_NO_ERR on success, appropriate dom_exception on failure.
 */
dom_exception _dom_html_options_collection_create(struct dom_html_document *doc,
		struct dom_node_internal *root,
		dom_callback_is_in_collection ic,
		void *ctx,
		struct dom_html_options_collection **col)
{
	*col = malloc(sizeof(dom_html_options_collection));
	if (*col == NULL)
		return DOM_NO_MEM_ERR;
	
	return _dom_html_options_collection_initialise(doc, *col, root,
			ic, ctx);
}

/**
 * Intialiase a dom_html_options_collection
 *
 * \param doc   The document
 * \param col   The collection object to be initialised
 * \param root  The root element of the collection
 * \param ic    The callback function used to determin whether certain node
 *              beint32_ts to the collection
 * \return DOM_NO_ERR on success.
 */
dom_exception _dom_html_options_collection_initialise(struct dom_html_document *doc,
		struct dom_html_options_collection *col,
		struct dom_node_internal *root,
		dom_callback_is_in_collection ic, void *ctx)
{
	return _dom_html_collection_initialise(doc, &col->base, root, ic, ctx);
}

/**
 * Finalise a dom_html_options_collection object
 *
 * \param col  The dom_html_options_collection object
 */
void _dom_html_options_collection_finalise(struct dom_html_options_collection *col)
{
	_dom_html_collection_finalise(&col->base);
}

/**
 * Destroy a dom_html_options_collection object
 * \param col  The dom_html_options_collection object
 */
void _dom_html_options_collection_destroy(struct dom_html_options_collection *col)
{
	_dom_html_options_collection_finalise(col);

	free(col);
}


/*-----------------------------------------------------------------------*/
/* Public API */

/**
 * Get the length of this dom_html_options_collection
 *
 * \param col  The dom_html_options_collection object
 * \param len  The returned length of this collection
 * \return DOM_NO_ERR on success.
 */
dom_exception dom_html_options_collection_get_length(dom_html_options_collection *col,
		uint32_t *len)
{
	return dom_html_collection_get_length(&col->base, len);
}

/**
 * Set the length of this dom_html_options_collection
 *
 * \param col  The dom_html_options_collection object
 * \param len  The length of this collection to be set
 * \return DOM_NO_ERR on success.
 */
dom_exception dom_html_options_collection_set_length(
		dom_html_options_collection *col, uint32_t len)
{
	UNUSED(col);
	UNUSED(len);

	/* TODO: how to deal with this */
	return DOM_NOT_SUPPORTED_ERR;
}

/**
 * Get the node with certain index
 *
 * \param col  The dom_html_options_collection object
 * \param index  The index number based on zero
 * \param node   The returned node object
 * \return DOM_NO_ERR on success.
 */
dom_exception dom_html_options_collection_item(dom_html_options_collection *col,
		uint32_t index, struct dom_node **node)
{
	return dom_html_collection_item(&col->base, index, node);
}

/**
 * Get the node in the collection according name
 *
 * \param col   The collection
 * \param name  The name of target node
 * \param node  The returned node object
 * \return DOM_NO_ERR on success.
 */
dom_exception dom_html_options_collection_named_item(dom_html_options_collection *col,
		dom_string *name, struct dom_node **node)
{
	struct dom_node_internal *n = col->base.root;
	dom_string *kname;
	dom_exception err;

	/* Search for an element with an appropriate ID */
	err = dom_html_collection_named_item(&col->base, name, node);
	if (err == DOM_NO_ERR && *node != NULL)
		return err;

	/* Didn't find one, so consider name attribute */
	err = dom_string_create_interned((const uint8_t *) "name", SLEN("name"),
			&kname);
	if (err != DOM_NO_ERR)
		return err;

	while (n != NULL) {
		if (n->type == DOM_ELEMENT_NODE &&
		    col->base.ic(n, col->base.ctx) == true) {
			dom_string *nval = NULL;

			err = dom_element_get_attribute(n, kname, &nval);
			if (err != DOM_NO_ERR) {
				dom_string_unref(kname);
				return err;
			}

			if (nval != NULL && dom_string_isequal(name, nval)) {
				*node = (struct dom_node *) n;
				dom_node_ref(n);
				dom_string_unref(nval);
				dom_string_unref(kname);

				return DOM_NO_ERR;
			}

			if (nval != NULL)
				dom_string_unref(nval);
		}

		/* Depth first iterating */
		if (n->first_child != NULL) {
			n = n->first_child;
		} else if (n->next != NULL) {
			n = n->next;
		} else {
			/* No children and siblings */
			struct dom_node_internal *parent = n->parent;

			while (n != col->base.root &&
					n == parent->last_child) {
				n = parent;
				parent = parent->parent;
			}
			
			if (n == col->base.root)
				n = NULL;
			else
				n = n->next;
		}
	}

	dom_string_unref(kname);

	/* Not found the target node */
	*node = NULL;

	return DOM_NO_ERR;
}

/**
 * Claim a reference on this collection
 *
 * \pram col  The collection object
 */
void dom_html_options_collection_ref(dom_html_options_collection *col)
{
	if (col == NULL)
		return;
	
	col->base.refcnt ++;
}

/**
 * Relese a reference on this collection
 *
 * \pram col  The collection object
 */
void dom_html_options_collection_unref(dom_html_options_collection *col)
{
	if (col == NULL)
		return;
	
	if (col->base.refcnt > 0)
		col->base.refcnt --;
	
	if (col->base.refcnt == 0)
		_dom_html_options_collection_destroy(col);
}

