package utils;

import java.io.IOException;
import java.io.StringReader;
import java.util.LinkedList;
import java.util.List;

import javax.xml.parsers.ParserConfigurationException;

import org.apache.commons.lang3.StringUtils;
import org.custommonkey.xmlunit.DetailedDiff;
import org.custommonkey.xmlunit.Diff;
import org.custommonkey.xmlunit.Difference;
import org.custommonkey.xmlunit.DifferenceConstants;
import org.custommonkey.xmlunit.XMLUnit;
import org.cyberneko.html.parsers.DOMParser;
import org.w3c.dom.Document;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import abstractdt.Getter;

public class UtilsDiff {

	/**
	 * transforms a string into a Document object.
	 * 
	 * @param html
	 *            the HTML string.
	 * @return The DOM Document version of the HTML string.
	 * @throws IOException
	 *             if an IO failure occurs.
	 * @throws SAXException
	 *             if an exception occurs while parsing the HTML string.
	 */
	public static Document asDocument(String html, boolean useNamespace) throws IOException {
		DOMParser domParser = new DOMParser();
		try {
			domParser.setProperty("http://cyberneko.org/html/properties/names/elems", "match");
			domParser.setFeature("http://xml.org/sax/features/namespaces", useNamespace);
			domParser.parse(new InputSource(new StringReader(html)));
		} catch (SAXException e) {
			throw new IOException("Error while reading HTML: " + html, e);
		}
		return domParser.getDocument();
	}

	/**
	 * BETA version of APOGEN-DOM-differencing mechanism
	 * 
	 * @param doc1
	 * @param doc2
	 * @return list of Differences
	 * @throws ParserConfigurationException
	 * @throws IOException
	 * @throws SAXException
	 */
	@SuppressWarnings("unchecked")
	public static List<Difference> customisedDomDiff(String string, String string2)
			throws ParserConfigurationException, SAXException, IOException {

		org.w3c.dom.Document doc1 = asDocument(string, true);
		org.w3c.dom.Document doc2 = asDocument(string2, true);

		XMLUnit.setControlParser("org.apache.xerces.jaxp.DocumentBuilderFactoryImpl");
		XMLUnit.setTestParser("org.apache.xerces.jaxp.DocumentBuilderFactoryImpl");
		XMLUnit.setSAXParserFactory("org.apache.xerces.jaxp.SAXParserFactoryImpl");

		XMLUnit.setNormalizeWhitespace(true);
		XMLUnit.setIgnoreAttributeOrder(true);
		XMLUnit.setIgnoreWhitespace(true);
		XMLUnit.setIgnoreComments(true);
		XMLUnit.setNormalize(true);
		XMLUnit.setIgnoreDiffBetweenTextAndCDATA(false);

		Diff d = new Diff(doc1, doc2);
		DetailedDiff dd = new DetailedDiff(d);

		dd.overrideDifferenceListener(new DomDifferenceListener());
		dd.overrideElementQualifier(null);

		return dd.getAllDifferences();
	}

	/**
	 * prints diff
	 * 
	 * @param target
	 * @param source
	 * @param diff
	 * @throws IOException
	 */
	public static List<abstractdt.Getter> printsDiff(String source, String target, List<Difference> diff)
			throws IOException {

		List<abstractdt.Getter> personalisedDiffList = new LinkedList<abstractdt.Getter>();

		// PRETTY PRINTS RESULTS BASED ON THE KIND OF DIFFERENCE
		for (Difference difference : diff) {

			abstractdt.Getter proposedDiff = null;

			switch (difference.getId()) {

			// DIFFERENT TEXT VALUES: using getNodeValue() to retrieve the text
			case DifferenceConstants.TEXT_VALUE_ID:

				if (!difference.getTestNodeDetail().getValue().equals("TITLE")
						&& !difference.getTestNodeDetail().getValue().equals("SCRIPT")) {

					String newLocator = difference.getTestNodeDetail().getXpathLocation();

					if (newLocator.contains("text()")) {
						newLocator = newLocator.substring(0, newLocator.indexOf("text()") - 1);
					}

					String name = UtilsStaticAnalyzer.getSmartNameForWebElementGetter(newLocator,
							UtilsStaticAnalyzer.getDOMFromDirectory(target), target);

					proposedDiff = new abstractdt.Getter(source, target, "textual change",
							difference.getControlNodeDetail().getNode().getTextContent().trim(),
							difference.getTestNodeDetail().getNode().getTextContent().trim(), newLocator, name);

					if (!personalisedDiffList.contains(proposedDiff)) {
						personalisedDiffList.add(proposedDiff);
					}

					break;
				}

				break;

			// DIFFERENT CHILDREN NUMBER: using simple getNode() to get what is going on
			case DifferenceConstants.CHILD_NODE_NOT_FOUND_ID:

				// System.out.println("[LOG] *** DIFFERENT NUMBER OF CHILDREN DETECTED " +
				// difference.getId() + "***");

				// adding
				if (StringUtils.isEmpty(difference.getControlNodeDetail().getXpathLocation())
						&& !difference.getTestNodeDetail().getValue().equals("BR")
						&& !difference.getTestNodeDetail().getValue().equals("SCRIPT")
						&& !difference.getTestNodeDetail().getValue().equals("TITLE")) {

					// System.out.println("\tADDED ELEMENT");
					// System.out.println("\tadded element:\t" +
					// difference.getTestNodeDetail().getNode());
					// System.out.println("\tlocator\t: " +
					// difference.getTestNodeDetail().getXpathLocation());

					String newLocator = difference.getTestNodeDetail().getXpathLocation();

					if (newLocator.contains("text()")) {
						newLocator = newLocator.substring(0, newLocator.indexOf("text()") - 1);
					}

					String name = UtilsStaticAnalyzer.getSmartNameForWebElementGetter(newLocator,
							UtilsStaticAnalyzer.getDOMFromDirectory(target), target);

					proposedDiff = new abstractdt.Getter(source, target, "added element", null,
							difference.getTestNodeDetail().getNode().getNodeName(), newLocator, name);

					// NB: the equals has been redefined on the locator field!
					if (!personalisedDiffList.contains(proposedDiff)) {
						personalisedDiffList.add(proposedDiff);
					}

				}
				// deleting
				else if (StringUtils.isEmpty(difference.getTestNodeDetail().getXpathLocation())) {
					/*
					 * System.out.println("\tREMOVED ELEMENT"); // TODO: trattare questo caso?
					 * System.out.println("\tbefore:\t" +
					 * difference.getControlNodeDetail().getNode()); System.out.println("\tafter:\t"
					 * + difference.getTestNodeDetail().getNode());
					 * System.out.println("\tlocator\t: " +
					 * difference.getTestNodeDetail().getXpathLocation());
					 * 
					 * newName = difference.getTestNodeDetail().getXpathLocation();
					 * if(newName.contains("text()")){ newName = newName.substring(0,
					 * newName.indexOf("text()")-1); }
					 * 
					 * name = UtilsStaticAnalyzer.getSmartNameForWebElement(newName,
					 * UtilsStaticAnalyzer.getDOMFromDirectory(target));
					 * 
					 * 
					 * personalisedDiffList.add( new abstractdt.Diff(source, target,
					 * "removed element",
					 * difference.getControlNodeDetail().getNode().getNodeValue(), null, null,
					 * name));
					 */
				} else {
					/*
					 * System.out.println("\tbefore:\t" +
					 * difference.getControlNodeDetail().getNode()); System.out.println("\tafter:\t"
					 * + difference.getTestNodeDetail().getNode());
					 * System.out.println("\tlocator\t: " +
					 * difference.getTestNodeDetail().getXpathLocation());
					 * 
					 * newName = difference.getTestNodeDetail().getXpathLocation();
					 * if(newName.contains("text()")){ newName = newName.substring(0,
					 * newName.indexOf("text()")-1); }
					 * 
					 * name = UtilsStaticAnalyzer.getSmartNameForWebElementGetter(newName,
					 * UtilsStaticAnalyzer.getDOMFromDirectory(target), target);
					 * 
					 * proposedDiff = new abstractdt.Diff(source, target, "modified element",
					 * difference.getControlNodeDetail().getNode().getNodeValue(),
					 * difference.getTestNodeDetail().getNode().getNodeValue(),
					 * difference.getTestNodeDetail().getXpathLocation(), name);
					 * 
					 * // NB: the equals has been redefined on the locator field!
					 * if(!personalisedDiffList.contains(proposedDiff)){
					 * personalisedDiffList.add(proposedDiff); }
					 */
				}

				break;

			}

		}
		return personalisedDiffList;
	}
}