/*
 * 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 "core/document.h"
#include "events/dispatch.h"
#include "events/mutation_event.h"

#include "utils/utils.h"

/**
 * Dispatch a DOMNodeInserted/DOMNodeRemoved event
 *
 * \param doc      The document object
 * \param et       The EventTarget object
 * \param type     "DOMNodeInserted" or "DOMNodeRemoved"
 * \param related  The parent of the removed/inserted node
 * \param success  Whether this event's default action get called
 * \return DOM_NO_ERR on success, appropriate dom_exception on failure.
 */
dom_exception __dom_dispatch_node_change_event(dom_document *doc,
		dom_event_target *et, dom_event_target *related, 
		dom_mutation_type change, bool *success)
{
	struct dom_mutation_event *evt;
	dom_string *type = NULL;
	dom_exception err;

	err = _dom_mutation_event_create(&evt);
	if (err != DOM_NO_ERR)
		return err;
	
	if (change == DOM_MUTATION_ADDITION) {
		type = dom_string_ref(doc->_memo_domnodeinserted);
	} else if (change == DOM_MUTATION_REMOVAL) {
		type = dom_string_ref(doc->_memo_domnoderemoved);
	} else {
		assert("Should never be here" == NULL);
	}

	/* Initialise the event with corresponding parameters */
	err = dom_mutation_event_init(evt, type, true, false, 
			related, NULL, NULL, NULL, change);
	dom_string_unref(type);
	if (err != DOM_NO_ERR) {
		goto cleanup;
	}

	err = dom_event_target_dispatch_event(et, evt, success);
	if (err != DOM_NO_ERR)
		goto cleanup;
	
cleanup:
	dom_event_unref(evt);

	return err;
}

/**
 * Dispatch a DOMNodeInsertedIntoDocument/DOMNodeRemovedFromDocument event
 *
 * \param doc      The document object
 * \param et       The EventTarget object
 * \param type     "DOMNodeInserted" or "DOMNodeRemoved"
 * \param success  Whether this event's default action get called
 * \return DOM_NO_ERR on success, appropriate dom_exception on failure.
 */
dom_exception __dom_dispatch_node_change_document_event(dom_document *doc,
		dom_event_target *et, dom_mutation_type change, bool *success)
{
	struct dom_mutation_event *evt;
	dom_string *type = NULL;
	dom_exception err;

	err = _dom_mutation_event_create(&evt);
	if (err != DOM_NO_ERR)
		return err;

	if (change == DOM_MUTATION_ADDITION) {
		type = dom_string_ref(doc->_memo_domnodeinsertedintodocument);
	} else if (change == DOM_MUTATION_REMOVAL) {
		type = dom_string_ref(doc->_memo_domnoderemovedfromdocument);
	} else {
		assert("Should never be here" == NULL);
	}

	/* Initialise the event with corresponding parameters */
	err = dom_mutation_event_init(evt, type, true, false, NULL,
			NULL, NULL, NULL, change);
	dom_string_unref(type);
	if (err != DOM_NO_ERR)
		goto cleanup;

	err = dom_event_target_dispatch_event(et, evt, success);
	if (err != DOM_NO_ERR)
		goto cleanup;
	
cleanup:
	dom_event_unref(evt);

	return err;
}

/**
 * Dispatch a DOMAttrModified event
 *
 * \param doc        The Document object
 * \param et         The EventTarget
 * \param prev       The previous value before change
 * \param new        The new value after change
 * \param related    The related EventTarget
 * \param attr_name  The Attribute name
 * \param change     How this attribute change
 * \param success    Whether this event's default handler get called
 * \return DOM_NO_ERR on success, appropirate dom_exception on failure.
 */
dom_exception __dom_dispatch_attr_modified_event(dom_document *doc,
		dom_event_target *et, dom_string *prev, dom_string *new,
		dom_event_target *related, dom_string *attr_name, 
		dom_mutation_type change, bool *success)
{
	struct dom_mutation_event *evt;
	dom_string *type = NULL;
	dom_exception err;

	err = _dom_mutation_event_create(&evt);
	if (err != DOM_NO_ERR)
		return err;
	
	type = dom_string_ref(doc->_memo_domattrmodified);

	/* Initialise the event with corresponding parameters */
	err = dom_mutation_event_init(evt, type, true, false, related, 
			prev, new, attr_name, change);
	dom_string_unref(type);
	if (err != DOM_NO_ERR) {
		goto cleanup;
	}

	err = dom_event_target_dispatch_event(et, evt, success);

cleanup:
	dom_event_unref(evt);

	return err;
}

/**
 * Dispatch a DOMCharacterDataModified event
 *
 * \param et       The EventTarget object
 * \param prev     The preValue of the DOMCharacterData
 * \param new      The newValue of the DOMCharacterData
 * \param success  Whether this event's default handler get called
 * \return DOM_NO_ERR on success, appropirate dom_exception on failure.
 *
 * TODO:
 * The character_data object may be a part of a Attr node, if so, another 
 * DOMAttrModified event should be dispatched, too. But for now, we did not
 * support any XML feature, so just leave it as this.
 */
dom_exception __dom_dispatch_characterdata_modified_event(
		dom_document *doc, dom_event_target *et,
		dom_string *prev, dom_string *new, bool *success)
{
	struct dom_mutation_event *evt;
	dom_string *type = NULL;
	dom_exception err;

	err = _dom_mutation_event_create(&evt);
	if (err != DOM_NO_ERR)
		return err;
	
	type = dom_string_ref(doc->_memo_domcharacterdatamodified);

	err = dom_mutation_event_init(evt, type, true, false, et, prev, 
			new, NULL, DOM_MUTATION_MODIFICATION);
	dom_string_unref(type);
	if (err != DOM_NO_ERR) {
		goto cleanup;
	}

	err = dom_event_target_dispatch_event(et, evt, success);

cleanup:
	dom_event_unref(evt);

	return err;
}

/**
 * Dispatch a DOMSubtreeModified event
 *
 * \param doc      The Document
 * \param et       The EventTarget object
 * \param success  Whether this event's default handler get called
 * \return DOM_NO_ERR on success, appropriate dom_exception on failure.
 */
dom_exception __dom_dispatch_subtree_modified_event(dom_document *doc,
		dom_event_target *et, bool *success)
{
	struct dom_mutation_event *evt;
	dom_string *type = NULL;
	dom_exception err;

	err = _dom_mutation_event_create(&evt);
	if (err != DOM_NO_ERR)
		return err;
	
	type = dom_string_ref(doc->_memo_domsubtreemodified);

	err = dom_mutation_event_init(evt, type, true, false, et, NULL, 
			NULL, NULL, DOM_MUTATION_MODIFICATION);
	dom_string_unref(type);
	if (err != DOM_NO_ERR) {
		goto cleanup;
	}

	err = dom_event_target_dispatch_event(et, evt, success);

cleanup:
	dom_event_unref(evt);

	return err;
}

/**
 * Dispatch a generic event
 *
 * \param doc         The Document
 * \param et          The EventTarget object
 * \param name        The name of the event
 * \param len         The length of the name string
 * \param bubble      Whether this event bubbles
 * \param cancelable  Whether this event can be cancelable
 * \param success     Whether this event's default handler get called
 * \return DOM_NO_ERR on success, appropriate dom_exception on failure.
 */
dom_exception _dom_dispatch_generic_event(dom_document *doc,
		dom_event_target *et, dom_string *event_name,
		bool bubble, bool cancelable, bool *success)
{
	struct dom_event *evt;
	dom_exception err;

	UNUSED(doc);

	err = _dom_event_create(&evt);
	if (err != DOM_NO_ERR)
		return err;
	
	err = dom_event_init(evt, event_name, bubble, cancelable);

	if (err != DOM_NO_ERR) {
		goto cleanup;
	}

	err = dom_event_target_dispatch_event(et, evt, success);

cleanup:
	dom_event_unref(evt);

	return err;
}

