/*******************************************************************************
 *
 * Copyright (c) 2000-2003 Intel Corporation
 * All rights reserved.
 * Copyright (c) 2012 France Telecom All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * - Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 * - Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 * - Neither name of Intel Corporation nor the names of its contributors
 * may be used to endorse or promote products derived from this software
 * without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 ******************************************************************************/

/*!
 * \file
 */

#include "ixml.h"
#include "ixmldebug.h"
#include "ixmlparser.h"

#include <stdlib.h>
#include <string.h>

#include "posix_overwrites.h" // IWYU pragma: keep

void ixmlDocument_init(IXML_Document *doc)
{
	memset(doc, 0, sizeof(IXML_Document));
}

void ixmlDocument_free(IXML_Document *doc)
{
	if (doc) {
		ixmlNode_free((IXML_Node *)doc);
	}
}

/*!
 * When this function is called first time, nodeptr is the root of the subtree,
 * so it is not necessary to do two steps recursion.
 *
 * Internal function called by ixmlDocument_importNode
 */
static void ixmlDocument_setOwnerDocument(
	/*! [in] The document node. */
	IXML_Document *doc,
	/*! [in] \todo documentation. */
	IXML_Node *nodeptr)
{
	if (nodeptr) {
		nodeptr->ownerDocument = doc;
		ixmlDocument_setOwnerDocument(
			doc, ixmlNode_getFirstChild(nodeptr));
		ixmlDocument_setOwnerDocument(
			doc, ixmlNode_getNextSibling(nodeptr));
	}
}

int ixmlDocument_importNode(
	IXML_Document *doc, IXML_Node *importNode, int deep, IXML_Node **rtNode)
{
	unsigned short nodeType;
	IXML_Node *newNode;

	*rtNode = NULL;

	if (!doc || !importNode) {
		return IXML_INVALID_PARAMETER;
	}

	nodeType = ixmlNode_getNodeType(importNode);
	if (nodeType == eDOCUMENT_NODE) {
		return IXML_NOT_SUPPORTED_ERR;
	}

	newNode = ixmlNode_cloneNode(importNode, deep);
	if (!newNode) {
		return IXML_FAILED;
	}

	ixmlDocument_setOwnerDocument(doc, newNode);
	*rtNode = newNode;

	return IXML_SUCCESS;
}

int ixmlDocument_createElementEx(
	IXML_Document *doc, const DOMString tagName, IXML_Element **rtElement)
{
	int errCode = IXML_SUCCESS;
	IXML_Element *newElement = NULL;

	if (!doc || !tagName) {
		errCode = IXML_INVALID_PARAMETER;
		goto ErrorHandler;
	}

	newElement = (IXML_Element *)malloc(sizeof(IXML_Element));
	if (!newElement) {
		errCode = IXML_INSUFFICIENT_MEMORY;
		goto ErrorHandler;
	}

	ixmlElement_init(newElement);
	newElement->tagName = strdup(tagName);
	if (!newElement->tagName) {
		ixmlElement_free(newElement);
		newElement = NULL;
		errCode = IXML_INSUFFICIENT_MEMORY;
		goto ErrorHandler;
	}
	/* set the node fields */
	newElement->n.nodeType = eELEMENT_NODE;
	newElement->n.nodeName = strdup(tagName);
	if (!newElement->n.nodeName) {
		free(newElement->tagName);
		ixmlElement_free(newElement);
		newElement = NULL;
		errCode = IXML_INSUFFICIENT_MEMORY;
		goto ErrorHandler;
	}

	newElement->n.ownerDocument = doc;

ErrorHandler:
	*rtElement = newElement;

	return errCode;
}

IXML_Element *ixmlDocument_createElement(
	IXML_Document *doc, const DOMString tagName)
{
	IXML_Element *newElement = NULL;
	int ret = IXML_SUCCESS;

	ret = ixmlDocument_createElementEx(doc, tagName, &newElement);
	if (ret != IXML_SUCCESS) {
		IxmlPrintf(__FILE__,
			__LINE__,
			"ixmlDocument_createElement",
			"Error %d\n",
			ret);
		return NULL;
	}
	return newElement;
}

int ixmlDocument_createDocumentEx(IXML_Document **rtDoc)
{
	IXML_Document *doc;
	int errCode = IXML_SUCCESS;

	doc = NULL;
	doc = (IXML_Document *)malloc(sizeof(IXML_Document));
	if (!doc) {
		errCode = IXML_INSUFFICIENT_MEMORY;
		goto ErrorHandler;
	}

	ixmlDocument_init(doc);

	doc->n.nodeName = strdup((const char *)DOCUMENTNODENAME);
	if (!doc->n.nodeName) {
		ixmlDocument_free(doc);
		doc = NULL;
		errCode = IXML_INSUFFICIENT_MEMORY;
		goto ErrorHandler;
	}

	doc->n.nodeType = eDOCUMENT_NODE;
	doc->n.ownerDocument = doc;

ErrorHandler:
	/* doc is NULL on failure */
	*rtDoc = doc;
	return errCode;
}

IXML_Document *ixmlDocument_createDocument(void)
{
	IXML_Document *doc = NULL;

	ixmlDocument_createDocumentEx(&doc);

	return doc;
}

int ixmlDocument_createTextNodeEx(
	IXML_Document *doc, const DOMString data, IXML_Node **textNode)
{
	IXML_Node *returnNode;
	int rc = IXML_SUCCESS;

	returnNode = NULL;
	if (!doc || !data) {
		rc = IXML_INVALID_PARAMETER;
		goto ErrorHandler;
	}

	returnNode = (IXML_Node *)malloc(sizeof(IXML_Node));
	if (!returnNode) {
		rc = IXML_INSUFFICIENT_MEMORY;
		goto ErrorHandler;
	}
	/* initialize the node */
	ixmlNode_init(returnNode);

	returnNode->nodeName = strdup((const char *)TEXTNODENAME);
	if (!returnNode->nodeName) {
		ixmlNode_free(returnNode);
		returnNode = NULL;
		rc = IXML_INSUFFICIENT_MEMORY;
		goto ErrorHandler;
	}
	/* add in node value */
	if (data) {
		returnNode->nodeValue = strdup(data);
		if (!returnNode->nodeValue) {
			ixmlNode_free(returnNode);
			returnNode = NULL;
			rc = IXML_INSUFFICIENT_MEMORY;
			goto ErrorHandler;
		}
	}

	returnNode->nodeType = eTEXT_NODE;
	returnNode->ownerDocument = doc;

ErrorHandler:
	*textNode = returnNode;
	return rc;
}

IXML_Node *ixmlDocument_createTextNode(IXML_Document *doc, const DOMString data)
{
	IXML_Node *returnNode = NULL;

	ixmlDocument_createTextNodeEx(doc, data, &returnNode);

	return returnNode;
}

int ixmlDocument_createAttributeEx(
	IXML_Document *doc, const DOMString name, IXML_Attr **rtAttr)
{
	int errCode = IXML_SUCCESS;
	IXML_Attr *attrNode = NULL;

	if (!doc || !name) {
		errCode = IXML_INVALID_PARAMETER;
		goto ErrorHandler;
	}
	attrNode = (IXML_Attr *)malloc(sizeof(IXML_Attr));
	if (!attrNode) {
		errCode = IXML_INSUFFICIENT_MEMORY;
		goto ErrorHandler;
	}
	ixmlAttr_init(attrNode);
	attrNode->n.nodeType = eATTRIBUTE_NODE;
	/* set the node fields */
	attrNode->n.nodeName = strdup(name);
	if (!attrNode->n.nodeName) {
		ixmlAttr_free(attrNode);
		attrNode = NULL;
		errCode = IXML_INSUFFICIENT_MEMORY;
		goto ErrorHandler;
	}
	attrNode->n.ownerDocument = doc;

ErrorHandler:
	*rtAttr = attrNode;
	return errCode;
}

IXML_Attr *ixmlDocument_createAttribute(
	IXML_Document *doc, const DOMString name)
{
	IXML_Attr *attrNode = NULL;

	if (ixmlDocument_createAttributeEx(doc, name, &attrNode) !=
		IXML_SUCCESS)
		return NULL;

	return attrNode;
}

int ixmlDocument_createAttributeNSEx(IXML_Document *doc,
	const DOMString namespaceURI,
	const DOMString qualifiedName,
	IXML_Attr **rtAttr)
{
	int errCode = IXML_SUCCESS;
	IXML_Attr *attrNode = NULL;

	if (!doc || !namespaceURI || !qualifiedName) {
		errCode = IXML_INVALID_PARAMETER;
		goto ErrorHandler;
	}

	errCode = ixmlDocument_createAttributeEx(doc, qualifiedName, &attrNode);
	if (errCode != IXML_SUCCESS) {
		goto ErrorHandler;
	}
	/* set the namespaceURI field */
	attrNode->n.namespaceURI = strdup(namespaceURI);
	if (!attrNode->n.namespaceURI) {
		ixmlAttr_free(attrNode);
		attrNode = NULL;
		errCode = IXML_INSUFFICIENT_MEMORY;
		goto ErrorHandler;
	}
	/* set the localName and prefix */
	errCode = ixmlNode_setNodeName((IXML_Node *)attrNode, qualifiedName);
	if (errCode != IXML_SUCCESS) {
		ixmlAttr_free(attrNode);
		attrNode = NULL;
		goto ErrorHandler;
	}

ErrorHandler:
	*rtAttr = attrNode;
	return errCode;
}

IXML_Attr *ixmlDocument_createAttributeNS(IXML_Document *doc,
	const DOMString namespaceURI,
	const DOMString qualifiedName)
{
	IXML_Attr *attrNode = NULL;

	ixmlDocument_createAttributeNSEx(
		doc, namespaceURI, qualifiedName, &attrNode);

	return attrNode;
}

int ixmlDocument_createCDATASectionEx(
	IXML_Document *doc, const DOMString data, IXML_CDATASection **rtCD)
{
	int errCode = IXML_SUCCESS;
	IXML_CDATASection *cDSectionNode = NULL;

	if (!doc || !data) {
		errCode = IXML_INVALID_PARAMETER;
		goto ErrorHandler;
	}

	cDSectionNode = (IXML_CDATASection *)malloc(sizeof(IXML_CDATASection));
	if (!cDSectionNode) {
		errCode = IXML_INSUFFICIENT_MEMORY;
		goto ErrorHandler;
	}

	ixmlCDATASection_init(cDSectionNode);
	cDSectionNode->n.nodeType = eCDATA_SECTION_NODE;
	cDSectionNode->n.nodeName = strdup((const char *)CDATANODENAME);
	if (!cDSectionNode->n.nodeName) {
		ixmlCDATASection_free(cDSectionNode);
		cDSectionNode = NULL;
		errCode = IXML_INSUFFICIENT_MEMORY;
		goto ErrorHandler;
	}

	cDSectionNode->n.nodeValue = strdup(data);
	if (!cDSectionNode->n.nodeValue) {
		ixmlCDATASection_free(cDSectionNode);
		cDSectionNode = NULL;
		errCode = IXML_INSUFFICIENT_MEMORY;
		goto ErrorHandler;
	}

	cDSectionNode->n.ownerDocument = doc;

ErrorHandler:
	*rtCD = cDSectionNode;
	return errCode;
}

IXML_CDATASection *ixmlDocument_createCDATASection(
	IXML_Document *doc, const DOMString data)
{
	IXML_CDATASection *cDSectionNode = NULL;

	ixmlDocument_createCDATASectionEx(doc, data, &cDSectionNode);

	return cDSectionNode;
}

int ixmlDocument_createElementNSEx(IXML_Document *doc,
	const DOMString namespaceURI,
	const DOMString qualifiedName,
	IXML_Element **rtElement)
{
	IXML_Element *newElement = NULL;
	int ret = IXML_SUCCESS;
	int line = 0;

	if (!doc || !namespaceURI || !qualifiedName) {
		line = __LINE__;
		ret = IXML_INVALID_PARAMETER;
		goto ErrorHandler;
	}

	ret = ixmlDocument_createElementEx(doc, qualifiedName, &newElement);
	if (ret != IXML_SUCCESS) {
		line = __LINE__;
		goto ErrorHandler;
	}
	/* set the namespaceURI field */
	newElement->n.namespaceURI = strdup(namespaceURI);
	if (!newElement->n.namespaceURI) {
		line = __LINE__;
		ixmlElement_free(newElement);
		newElement = NULL;
		ret = IXML_INSUFFICIENT_MEMORY;
		goto ErrorHandler;
	}
	/* set the localName and prefix */
	ret = ixmlNode_setNodeName((IXML_Node *)newElement, qualifiedName);
	if (ret != IXML_SUCCESS) {
		line = __LINE__;
		ixmlElement_free(newElement);
		newElement = NULL;
		ret = IXML_INSUFFICIENT_MEMORY;
		goto ErrorHandler;
	}

	newElement->n.nodeValue = NULL;

ErrorHandler:
	*rtElement = newElement;
	if (ret != IXML_SUCCESS) {
		IxmlPrintf(__FILE__,
			line,
			"ixmlDocument_createElementNSEx",
			"Error %d\n",
			ret);
	}

	return ret;
}

IXML_Element *ixmlDocument_createElementNS(IXML_Document *doc,
	const DOMString namespaceURI,
	const DOMString qualifiedName)
{
	IXML_Element *newElement = NULL;

	ixmlDocument_createElementNSEx(
		doc, namespaceURI, qualifiedName, &newElement);

	return newElement;
}

IXML_NodeList *ixmlDocument_getElementsByTagName(
	IXML_Document *doc, const DOMString tagName)
{
	IXML_NodeList *returnNodeList = NULL;

	if (!doc || !tagName) {
		return NULL;
	}

	ixmlNode_getElementsByTagName(
		(IXML_Node *)doc, tagName, &returnNodeList);

	return returnNodeList;
}

IXML_NodeList *ixmlDocument_getElementsByTagNameNS(IXML_Document *doc,
	const DOMString namespaceURI,
	const DOMString localName)
{
	IXML_NodeList *returnNodeList = NULL;

	if (!doc || !namespaceURI || !localName) {
		return NULL;
	}

	ixmlNode_getElementsByTagNameNS(
		(IXML_Node *)doc, namespaceURI, localName, &returnNodeList);

	return returnNodeList;
}

IXML_Element *ixmlDocument_getElementById(
	IXML_Document *doc, const DOMString tagName)
{
	IXML_Element *rtElement = NULL;
	IXML_Node *nodeptr = (IXML_Node *)doc;
	const char *name;

	if (!nodeptr || !tagName) {
		return rtElement;
	}

	if (ixmlNode_getNodeType(nodeptr) == eELEMENT_NODE) {
		name = ixmlNode_getNodeName(nodeptr);
		if (!name) {
			return rtElement;
		}

		if (strcmp(tagName, name) == 0) {
			rtElement = (IXML_Element *)nodeptr;
			return rtElement;
		} else {
			rtElement = ixmlDocument_getElementById(
				(IXML_Document *)ixmlNode_getFirstChild(
					nodeptr),
				tagName);
			if (!rtElement) {
				rtElement = ixmlDocument_getElementById(
					(IXML_Document *)
						ixmlNode_getNextSibling(
							nodeptr),
					tagName);
			}
		}
	} else {
		rtElement = ixmlDocument_getElementById(
			(IXML_Document *)ixmlNode_getFirstChild(nodeptr),
			tagName);
		if (!rtElement) {
			rtElement = ixmlDocument_getElementById(
				(IXML_Document *)ixmlNode_getNextSibling(
					nodeptr),
				tagName);
		}
	}

	return rtElement;
}
