/*
 * xml - A plugin for xml objects for the opensync framework
 * Copyright (C) 2004-2005  Armin Bauer <armin.bauer@opensync.org>
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
 * 
 */

#include "opensync/opensync.h" 
#include "opensync/opensync_time_internals.h"
#include "xml-support.h"
#include <glib.h>

static char *osxml_prepare_time(const char *content, xmlNode *node) {

	osync_trace(TRACE_ENTRY, "%s(%s, %p)", __func__, content, node);

	int tzoffset = 0;
	char *time = NULL;
	struct tm *ttm = NULL;


	if (!osync_time_isutc(content)) {
		time = osync_time_tzlocal2utc(node, (char *) node->name); 
		if (!time) {
			ttm = osync_time_vtime2tm(content);
			tzoffset = osync_time_timezone_diff(ttm); 
			time = osync_time_vtime2utc(content, tzoffset);
			g_free(ttm);
		}
	}

	if (!time)
		time = g_strdup(content);

	osync_trace(TRACE_EXIT, "%s: %s", __func__, time);
	return time;
}

static osync_bool osxml_compare_time(xmlNode *leftnode, xmlNode *rightnode) {

	osync_trace(TRACE_ENTRY, "%s(%s(%p), %s(%p))", __func__, leftnode->name, leftnode, rightnode->name, rightnode);
	int ret = 0;
	char *left = NULL, *right = NULL;
	char *leftcontent = osxml_find_node(leftnode, "Content");
	char *rightcontent = osxml_find_node(rightnode, "Content");

	osync_trace(TRACE_SENSITIVE, "time compare - left: %s right: %s", leftcontent, rightcontent);

	if (osync_time_isutc(leftcontent) != osync_time_isutc(rightcontent)) {
		left = osxml_prepare_time(leftcontent, leftnode);
		right = osxml_prepare_time(rightcontent, rightnode);

		g_free(leftcontent);
		g_free(rightcontent);

		osync_trace(TRACE_SENSITIVE, "AFTER convert - left: %s right: %s", left, right);
	} else {
		left = leftcontent;
		right = rightcontent;
	}

	ret = strcmp(left, right);

	g_free(left);
	g_free(right);

	if (ret) {
		osync_trace(TRACE_EXIT, "%s: FALSE", __func__);
		return FALSE;
	}

	osync_trace(TRACE_EXIT, "%s: TRUE", __func__);
	return TRUE; 

}

static osync_bool osxml_compare_node(xmlNode *leftnode, xmlNode *rightnode)
{
	osync_trace(TRACE_ENTRY, "%s(%p:%s, %p:%s)", __func__, leftnode, leftnode->name, rightnode, rightnode->name);

	if (strcmp((char*)leftnode->name, (char*)rightnode->name)) {
		osync_trace(TRACE_EXIT, "%s: FALSE: Different Name", __func__);
		return FALSE;
	}
	
	leftnode = leftnode->children;
	rightnode = rightnode->children;
	xmlNode *rightstartnode = rightnode;
	
	if (!leftnode && !rightnode) {
		osync_trace(TRACE_EXIT, "%s: TRUE. Both 0", __func__);
		return TRUE;
	}
	
	if (!leftnode || !rightnode) {
		osync_trace(TRACE_EXIT, "%s: FALSE. One 0", __func__);
		return FALSE;
	}
	
	do {
		if (!strcmp("UnknownParam", (char*)leftnode->name))
			continue;
		if (!strcmp("Order", (char*)leftnode->name))
			continue;
		rightnode = rightstartnode;
		char *leftcontent = (char*)xmlNodeGetContent(leftnode);
		
		do {
			if (!strcmp("UnknownParam", (char*)rightnode->name))
				continue;

			osync_trace(TRACE_INTERNAL, "leftnode %s, rightnode %s", leftnode->name, rightnode->name);

		       /* Compare only nodes with same name. Skip if
			* the names are different
			*/
		       if (xmlStrcmp(leftnode->name, rightnode->name))
			       continue;

			char *rightcontent = (char*)xmlNodeGetContent(rightnode);

			osync_trace(TRACE_SENSITIVE, "leftcontent %s, rightcontent %s\n", leftcontent, rightcontent);
			
			if (leftcontent == rightcontent) {
				g_free(rightcontent);
				goto next;
			}

			/* We compare the striped content to work around bugs in
			 * applications like evo2 which always strip the content
			 * and would therefore cause conflicts. This change should not break
			 * anything since it does not touch the actual content */			
			char *strip_right = g_strstrip(g_strdup(rightcontent));
			char *strip_left = g_strstrip(g_strdup(leftcontent));
			if (!strcmp(strip_left, strip_right)) {
				g_free(strip_right);
				g_free(strip_left);
				g_free(rightcontent);
				goto next;
			}
			g_free(strip_right);
			g_free(strip_left);

			if (!leftcontent || !rightcontent) {
				osync_trace(TRACE_EXIT, "%s: One is empty", __func__);
				return FALSE;
			}

			/* Workaround for palm-sync. palm-sync is not able to set a correct Completed date-timestamp so ignore value (objtype: todo) */ 
			if (!strcmp("Completed", (char*)rightnode->name) && !strcmp("Completed",(char*)leftnode->name)) {
				if ((leftcontent && rightcontent) || (!leftcontent && !rightcontent)) {
					osync_trace(TRACE_INTERNAL, "PALM-SYNC workaround active!");
					g_free(rightcontent);
					goto next;
				}
			}


			g_free(rightcontent);

			if ((!strcmp("DateStarted", (char*)rightnode->name) && !strcmp("DateStarted", (char*)leftnode->name))
				|| (!strcmp("DateEnd", (char*)rightnode->name) && !strcmp("DateEnd", (char*)leftnode->name))) {
				
				if (osxml_compare_time(leftnode, rightnode))
					goto next;
			}

			/* compare child nodes again .... */
			if (rightnode->type == XML_ELEMENT_NODE && osxml_compare_node(rightnode, leftnode))
					goto next;


		} while ((rightnode = rightnode->next));
		osync_trace(TRACE_EXIT, "%s: Could not match one", __func__);
		g_free(leftcontent);
		return FALSE;
		next:;
		g_free(leftcontent);
	} while ((leftnode = leftnode->next));
	
	osync_trace(TRACE_EXIT, "%s: TRUE", __func__);
	return TRUE;
}

OSyncConvCmpResult osxml_compare(xmlDoc *leftinpdoc, xmlDoc *rightinpdoc, OSyncXMLScore *scores, int default_score, int treshold)
{
	osync_trace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, leftinpdoc, rightinpdoc, scores);
	int z = 0, i = 0, n = 0;
	int res_score = 0;
	
	xmlDoc *leftdoc = xmlCopyDoc(leftinpdoc, TRUE);
	xmlDoc *rightdoc = xmlCopyDoc(rightinpdoc, TRUE);
	
	osync_trace(TRACE_INTERNAL, "Comparing given score list");
	while (scores && scores[z].path) {
		OSyncXMLScore *score = &scores[z];
		z++;
		xmlXPathObject *leftxobj = osxml_get_nodeset(leftdoc, score->path);
		xmlXPathObject *rightxobj = osxml_get_nodeset(rightdoc, score->path);
		
		xmlNodeSet *lnodes = leftxobj->nodesetval;
		xmlNodeSet *rnodes = rightxobj->nodesetval;
		
		int lsize = (lnodes) ? lnodes->nodeNr : 0;
		int rsize = (rnodes) ? rnodes->nodeNr : 0;
		osync_trace(TRACE_INTERNAL, "parsing next path %s", score->path);
		
		if (!score->value) {
			for (i = 0; i < lsize; i++) {
				xmlUnlinkNode(lnodes->nodeTab[i]);
				xmlFreeNode(lnodes->nodeTab[i]);
				lnodes->nodeTab[i] = NULL;
			}
			
			for (n = 0; n < rsize; n++) {
				xmlUnlinkNode(rnodes->nodeTab[n]);
				xmlFreeNode(rnodes->nodeTab[n]);
				rnodes->nodeTab[n] = NULL;
			}
		} else {
			for (i = 0; i < lsize; i++) {
				if (!lnodes->nodeTab[i])
					continue;
				for (n = 0; n < rsize; n++) {
					if (!rnodes->nodeTab[n])
						continue;
					osync_trace(TRACE_INTERNAL, "cmp %i:%s (leftcontent), %i:%s (rightcontent)", i, lnodes->nodeTab[i]->name,  
							n, rnodes->nodeTab[n]->name);
					osync_trace(TRACE_SENSITIVE, "cmp %i:%s (%s), %i:%s (%s)\n", i, lnodes->nodeTab[i]->name, osxml_find_node(lnodes->nodeTab[i], 
							"Content"), n, rnodes->nodeTab[n]->name, osxml_find_node(rnodes->nodeTab[n], "Content"));

					/*
					if ((!strcmp("DateStarted", lnodes->nodeTab[i]->name) && !strcmp("DateStarted", rnodes->nodeTab[n]->name))
							|| (!strcmp("DateEnd", lnodes->nodeTab[i]->name) && !strcmp("DateEnd", rnodes->nodeTab[n]->name))) {
					*/		

					if (osxml_compare_node(lnodes->nodeTab[i], rnodes->nodeTab[n])) {
						osync_trace(TRACE_INTERNAL, "Adding %i for %s", score->value, score->path);
						res_score += score->value;
						xmlUnlinkNode(lnodes->nodeTab[i]);
						xmlFreeNode(lnodes->nodeTab[i]);
						lnodes->nodeTab[i] = NULL;
						xmlUnlinkNode(rnodes->nodeTab[n]);
						xmlFreeNode(rnodes->nodeTab[n]);
						rnodes->nodeTab[n] = NULL;
						goto next;
					}
				}
				osync_trace(TRACE_INTERNAL, "Subtracting %i for %s", score->value, score->path);
				res_score -= score->value;
				next:;
			}
			for(i = 0; i < rsize; i++) {
				if (!rnodes->nodeTab[i])
					continue;
				res_score -= score->value;
			}
		}
		
		xmlXPathFreeObject(leftxobj);
		xmlXPathFreeObject(rightxobj);
	}
	
	xmlXPathObject *leftxobj = osxml_get_nodeset(leftdoc, "/*/*");
	xmlXPathObject *rightxobj = osxml_get_nodeset(rightdoc, "/*/*");
	
	xmlNodeSet *lnodes = leftxobj->nodesetval;
	xmlNodeSet *rnodes = rightxobj->nodesetval;

	// Check if nodeTab actually exists (for example vnote stuff would crash otherwise...)
	if (lnodes->nodeTab && rnodes->nodeTab) {
		// WORKAROUND - FIXME
		// if nodeTab[0] is an Event or Todo we need a new node structure (/*/*/*)

		if ((!strcmp((char*)lnodes->nodeTab[0]->name, "Event") && \
			!strcmp((char*)rnodes->nodeTab[0]->name, "Event")) || \
		    (!strcmp((char*)lnodes->nodeTab[0]->name, "Todo") && \
			!strcmp((char*)rnodes->nodeTab[0]->name, "Todo"))) {

			xmlXPathFreeObject(leftxobj);
			xmlXPathFreeObject(rightxobj);

			leftxobj = osxml_get_nodeset(leftdoc, "/*/*/*");
			rightxobj = osxml_get_nodeset(rightdoc, "/*/*/*");

			lnodes = leftxobj->nodesetval;
			rnodes = rightxobj->nodesetval;

		}
	}

	int lsize = (lnodes) ? lnodes->nodeNr : 0;
	int rsize = (rnodes) ? rnodes->nodeNr : 0;
	
	osync_trace(TRACE_INTERNAL, "Comparing remaining list");
	osync_bool same = TRUE;
	for(i = 0; i < lsize; i++) {		
		for (n = 0; n < rsize; n++) {
			if (!rnodes->nodeTab[n])
				continue;
			osync_trace(TRACE_INTERNAL, "cmp %i:%s (leftcontent), %i:%s (rightcontent)", i, lnodes->nodeTab[i]->name,  
							n, rnodes->nodeTab[n]->name);
			osync_trace(TRACE_SENSITIVE, "cmp %i:%s (%s), %i:%s (%s)\n", i, lnodes->nodeTab[i]->name, osxml_find_node(lnodes->nodeTab[i],
					"Content"), n, rnodes->nodeTab[n]->name, osxml_find_node(rnodes->nodeTab[n], "Content"));

			if ((!strcmp("DateStarted", (char *) lnodes->nodeTab[i]->name) && !strcmp("DateStarted", (char *) rnodes->nodeTab[n]->name))
					|| (!strcmp("DateEnd", (char *) lnodes->nodeTab[i]->name) && !strcmp("DateEnd", (char *) rnodes->nodeTab[n]->name))) {

				if (osxml_compare_time(lnodes->nodeTab[i], rnodes->nodeTab[n])) {
					xmlUnlinkNode(lnodes->nodeTab[i]);
					xmlFreeNode(lnodes->nodeTab[i]);
					lnodes->nodeTab[i] = NULL;
					xmlUnlinkNode(rnodes->nodeTab[n]);
					xmlFreeNode(rnodes->nodeTab[n]);
					rnodes->nodeTab[n] = NULL;
					osync_trace(TRACE_INTERNAL, "Adding %i", default_score);
					res_score += default_score;
					goto next2;
				}
			}

			if (osxml_compare_node(lnodes->nodeTab[i], rnodes->nodeTab[n])) {
				xmlUnlinkNode(lnodes->nodeTab[i]);
				xmlFreeNode(lnodes->nodeTab[i]);
				lnodes->nodeTab[i] = NULL;
				xmlUnlinkNode(rnodes->nodeTab[n]);
				xmlFreeNode(rnodes->nodeTab[n]);
				rnodes->nodeTab[n] = NULL;
				osync_trace(TRACE_INTERNAL, "Adding %i", default_score);
				res_score += default_score;
				goto next2;
			}
		}
		osync_trace(TRACE_INTERNAL, "Subtracting %i", default_score);
		res_score -= default_score;

		// XXX Find a better way to workaroudn the problem of ignoring without unlinking nodes.
		if (!strcmp("Timezone", (char *) lnodes->nodeTab[i]->name))
			osync_trace(TRACE_INTERNAL, "Workaround for Timezone field. We ignore it but don't unlink it from XML");
		else
			same = FALSE;

		//goto out;
		next2:;
	}
	
	for(i = 0; i < lsize; i++) {
		if (!lnodes->nodeTab[i])
			continue;
		osync_trace(TRACE_INTERNAL, "left remaining: %s", lnodes->nodeTab[i]->name);

		// XXX Find a better way to workaroudn the problem of ignoring without unlinking nodes.
		if (!strcmp("Timezone", (char *) lnodes->nodeTab[i]->name))
			osync_trace(TRACE_INTERNAL, "Workaround for Timezone field. We ignore it but don't unlink it from XML");
		else
			same = FALSE;

		goto out;
	}
	
	for(i = 0; i < rsize; i++) {
		if (!rnodes->nodeTab[i])
			continue;
		osync_trace(TRACE_INTERNAL, "right remaining: %s", rnodes->nodeTab[i]->name);

		// XXX Find a better way to workaroudn the problem of ignoring without unlinking nodes.
		if (!strcmp("Timezone", (char *) rnodes->nodeTab[i]->name))
			osync_trace(TRACE_INTERNAL, "Workaround for Timezone field. We ignore it but don't unlink it from XML");
		else
			same = FALSE;

		goto out;
	}
	out:
	xmlXPathFreeObject(leftxobj);
	xmlXPathFreeObject(rightxobj);

	xmlFreeDoc(leftdoc);
	xmlFreeDoc(rightdoc);

	osync_trace(TRACE_INTERNAL, "Result is: %i, Treshold is: %i", res_score, treshold);
	if (same) {
		osync_trace(TRACE_EXIT, "%s: SAME", __func__);
		return CONV_DATA_SAME;
	}
	if (res_score >= treshold) {
		osync_trace(TRACE_EXIT, "%s: SIMILAR", __func__);
		return CONV_DATA_SIMILAR;
	}
	osync_trace(TRACE_EXIT, "%s: MISMATCH", __func__);
	return CONV_DATA_MISMATCH;
}



syntax highlighted by Code2HTML, v. 0.9.1