package com.ejie.ab61.comun.utilidades;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.StringTokenizer;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.ProcessingInstruction;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import com.ejie.ab61.comun.Constantes;

/**
 * 
 *  
 * 
 */
public class DOMUtilis {

	/**
	 * 
	 * 
	 * 
	 *  
	 * 
	 */
	private DOMUtilis() {
	}

	/**
	 * Crea un objeto de la clase Document a partir del String XML que recibe
	 * como parámetro
	 * 
	 * @param xml
	 *            String que contiene el XML
	 * @return Document Objeto de la clase Document
	 * @throws Exception
	 *             Exception
	 */
	public static Document createDoc(String xml) throws Exception {
		try {
			return (DOMUtilis.buildDocument(xml, "8859_1"));
		} catch (UnsupportedEncodingException e) {
			throw new Exception("Error createDoc", e);
		} catch (SAXException e) {
			throw new Exception("Error createDoc", e);
		} catch (IOException e) {
			throw new Exception("Error createDoc", e);
		} catch (ParserConfigurationException e) {
			throw new Exception("Error createDoc", e);
		}
	}

	public static Document fncCrearDoc(String xml) throws Exception {

		Document doc = null;
		DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
		// try {
		DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
		try {
			doc = documentBuilder.parse((InputStream) new ByteArrayInputStream(xml.getBytes("8859_1")));
		} catch (SAXException sax) {

			throw sax;
		}

		// finally{
		return doc;
		// }
	}

	/**
	 * Crea un objeto de la clase Document a partir del String XML que recibe
	 * como parámetro
	 * 
	 * @param xml
	 *            String que contiene el XML UTF-8
	 * @return Document Objeto de la clase Document
	 * @throws Exception
	 *             Exception
	 */
	public static Document createDocUTF8(String xml) throws Exception {
		try {
			return (DOMUtilis.buildDocument(xml, "UTF-8"));
		} catch (UnsupportedEncodingException e) {
			throw new Exception("Error createDocUTF8", e);
		} catch (SAXException e) {
			throw new Exception("Error createDocUTF8", e);
		} catch (IOException e) {
			throw new Exception("Error createDocUTF8", e);
		} catch (ParserConfigurationException e) {
			throw new Exception("Error createDocUTF8", e);
		}
	}

	/**
	 * Crea un objeto document a partir de XML con la codificación enviada
	 * 
	 * @param strPfXML
	 *            String con el XML
	 * @param strPfEnconding
	 *            Codificacion enviada
	 * @return Document Objeto de la clase Document
	 * @throws ParserConfigurationException
	 *             excepcion de parseo
	 * @throws UnsupportedEncodingException
	 *             encode
	 * @throws IOException
	 *             IO
	 * @throws SAXException
	 *             error de sax
	 */
	private static Document buildDocument(String strPfXML, String strPfEnconding)
			throws ParserConfigurationException, UnsupportedEncodingException, IOException, SAXException {
		Document doc = null;
		DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();

		DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();

		doc = documentBuilder.parse((InputStream) new ByteArrayInputStream(strPfXML.getBytes(strPfEnconding)));

		return (doc);
	}

	/**
	 * Recupera el valor de un elemento
	 * 
	 * @param elemento
	 *            Elemento del que se recuperar el valor @return String Valor
	 *            del elemento @throws
	 */
	public static String retrieveElementValue(Element elemento) {
		return (elemento.getChildNodes().item(0).getNodeValue());
	}

	/**
	 * Recupera el valor del nodo de un elemento
	 * 
	 * @param nodo
	 *            Nodo del que se recuperar el valor @return String Valor del
	 *            nodo @throws
	 */
	public static String retrieveNodeValue(Node nodo) {
		return nodo.getChildNodes().item(0).getNodeValue();

	}

	/**
	 * Recupera el valor de una etiqueta (nodo) de un documento
	 * 
	 * @param document
	 *            Documento del que se recupera el valor de la etiqueta
	 * @param elementUrl
	 *            Ruta del nodo dentro del documento, separado por '.'
	 * @return String Valor de la etiqueta (nodo)
	 * @throws Exception
	 */
	public static String retrieveElementValue(Document document, String elementUrl) {
		String result = null;
		StringTokenizer strTok = null;
		String block = null;
		NodeList nodeList = null;
		Element element = null;
		int cont = 0;
		try {
			strTok = new StringTokenizer(elementUrl, ".");
			while (strTok.hasMoreTokens()) {
				block = strTok.nextToken();
				if (cont == 0) {
					nodeList = document.getElementsByTagName(block);
				} else {
					nodeList = element.getElementsByTagName(block);
				}
				element = (Element) nodeList.item(0);
				if (element == null) {
					return null;
				}
				cont++;
			}
			if (nodeList.item(0).getChildNodes().item(0) != null) {
				result = (nodeList.item(0).getChildNodes().item(0)).getNodeValue();
			}
		} catch (Throwable e) { // NOPMD by ifernandez on 14/08/13 9:51

			return null;
		}
		return (result);
	}

	/**
	 * Recupera el valor de una etiqueta (nodo) de un documento Prerequsito: El
	 * primer nodo que se indica en la ruta ha de ser único
	 * 
	 * @param document
	 *            Documento del que se recupera el valor de la etiqueta
	 * @param path
	 *            Ruta del nodo dentro del documento, separado por '.'
	 * @return String Valor de la etiqueta (nodo)
	 * @throws Exception
	 */
	public static String retrieveElementValueByPath(Document document, String path) {

		String result = null;
		StringTokenizer strTok = null;
		String block = null;
		NodeList nodeList = null;
		// Element element = null;
		// String strNodeName = "";
		String strNodeValue = "";
		boolean blNodeFound = false;
		int cont = 0;
		strTok = new StringTokenizer(path, ".");
		try {
			while (strTok.hasMoreTokens()) {
				// strNodeName = "[nodo vacio]";
				block = strTok.nextToken();

				if (cont == 0) {
					nodeList = document.getElementsByTagName(block).item(0).getChildNodes();
					// strNodeName =
					// document.getElementsByTagName(block).item(0).getNodeName();

				} else {
					blNodeFound = false;

					for (int i = 0; i < nodeList.getLength(); i++) {

						if (block.equals(nodeList.item(i).getNodeName())) {
							// strNodeName = nodeList.item(i).getNodeName();
							strNodeValue = nodeList.item(i).getNodeValue();
							if (nodeList.item(i).hasChildNodes()) {

								nodeList = nodeList.item(i).getChildNodes();
							} else {

								return strNodeValue;
							}
							blNodeFound = true;
							break;
						} // end if
					} // end for

					if (!blNodeFound) {
						return null;
					} // end if

				} // end else

				cont++;
			} // end while
			if (nodeList.item(0) != null && nodeList.item(0).getNodeValue() != null) {
				result = nodeList.item(0).getNodeValue();
			}
		} catch (Exception e) {

			return null;
		}
		return (result);
	}

	/**
	 * Recupera la lista de nodos de una etiqueta
	 * 
	 * @param document
	 *            El documento del que se recupera la lista de nodos
	 * @param elementUrl
	 *            Ruta de la etiqueta dentro del documento, separado por '.'
	 * @return NodeList Lista de nodos
	 * @throws Exception
	 */
	public static NodeList retrieveElementsList(Document document, String elementUrl) {
		NodeList listaResultado = null;
		StringTokenizer tokCadena = null;
		String bloque = null;
		NodeList listaNodos = null;
		Element elemento = null;
		int contador = 0;
		tokCadena = new StringTokenizer(elementUrl, ".");
		while (tokCadena.hasMoreTokens()) {
			bloque = tokCadena.nextToken();
			if (contador == 0) {
				listaNodos = document.getElementsByTagName(bloque);
			} else {
				listaNodos = elemento.getElementsByTagName(bloque);
			}
			elemento = (Element) listaNodos.item(0);
			if (elemento == null) {
				return null;
			}
			contador++;
		}
		if (listaNodos != null) {
			listaResultado = listaNodos;
		}
		return (listaResultado);
	}

	/**
	 * Devuelve un nodo de una lista de nodos
	 * 
	 * @param nodelist
	 *            Lista de nodos
	 * @param element
	 *            Entero con el elemento de la lista de nodos
	 * @param node
	 *            nodo
	 * @return nodo El nodo dentro del elemento
	 * @throws Exception
	 */
	public static Node retrieveNode(NodeList nodelist, int element, int node) {
		return nodelist.item(element).getChildNodes().item(node);
	}

	/**
	 * Devuelve el valor de un atributo
	 * 
	 * @param node
	 *            Nodo que contiene el atributo
	 * @param strPfAttribute
	 *            String con el nombre del atributo
	 * @return String Valor del atributo
	 * @throws Exception
	 */
	public static String attributeValue(Node node, String strPfAttribute) {
		if (node.hasAttributes()) {
			return node.getAttributes().getNamedItem(strPfAttribute).getNodeValue();
		} else {
			return ("");
		}
	}

	/**
	 * Devuelve el String a partir de un Document
	 * 
	 * @param doc
	 *            Documento que contiene el XML
	 * @return String que contiene el XML
	 * @throws Exception
	 *             Exception
	 */
	public static String getStringDoc(Document doc) throws Exception {
		try {
			TransformerFactory tFactory = TransformerFactory.newInstance();
			Transformer transformer = tFactory.newTransformer();
			DOMSource source = new DOMSource(doc);
			StringWriter sw = new StringWriter();
			StreamResult result = new StreamResult(sw);
			transformer.transform(source, result);
			String xmlString = sw.toString();
			return (xmlString);
		} catch (TransformerConfigurationException e) {
			throw new Exception("Error getStringDoc", e);
		} catch (TransformerException e) {
			throw new Exception("Error getStringDoc", e);
		}

	}

	/**
	 * Parsea un documento XML
	 * 
	 * @param xml
	 *            String que contiene el XML
	 * @param s
	 *            String
	 * @return String Documento XML parseado
	 * @throws Exception
	 *             Exception
	 */
	public static String parserXML(String xml, String s) throws Exception {
		int i = xml.indexOf("<" + s + ">");
		if (i == -1) {
			return "NOEXISTE";
		}
		String salida = xml.substring(i + s.length() + 2, xml.indexOf("</" + s + ">"));
		return (salida);
	}

	/**
	 * Método que aplica la construcción CDATA (Character Data) a los datos
	 * pasados como parámetro para informar al procesador de XML de que no hay
	 * marcado en los caracteres que contiene la sección.
	 * 
	 * @param data
	 *            Datos a meter en la estructura CDATA.
	 * @return String
	 */
	public static String xmlData(String data) {
		return "<![CDATA[" + data + "]]>";
	}

	/**
	 * quita los \n que vienen en los XML de Dokusi e interoperabilidad
	 * 
	 * @param xml
	 *            : xml de entrada
	 * @return xml : xml de salida
	 */
	public static String parsearStringDokusi(String xml) {

		StringTokenizer TokenizerXML = new StringTokenizer(xml, "\n");

		StringBuffer Sbxml = new StringBuffer();
		String Sbxml2 = "";

		while (TokenizerXML.hasMoreElements()) {
			Sbxml2 = TokenizerXML.nextToken();
			Sbxml.append(Sbxml2.trim());
		}
		return Sbxml.toString();

	}

	/**
	 * 
	 * @param datosDocumento
	 *            Document
	 * @param taskIDColas
	 *            String
	 * 
	 * @return String
	 */
	public static String retriveElementColas(Document datosDocumento, String taskIDColas) {
		String resultadoGuid = "";

		HashMap<String, String> myHash = new HashMap<String, String>();
		// HashMap<String, String> myHashVacio = new HashMap<String, String>();

		NodeList nodosPadre = datosDocumento.getElementsByTagName(Constantes.MESG_LIST_BD); // messageListBD
		int numNodosPadre = nodosPadre.item(Constantes.MAGIC_NUMBER_0).getChildNodes().getLength();
		if (numNodosPadre != Constantes.MAGIC_NUMBER_0) {
			for (int i = 0; i < numNodosPadre; i++) { // Vienen X messageBD
														// por cada etiqueta
														// dentro de cada
														// messageBD uno recojo
														// su name
														// y su value.
				NodeList nodos2 = nodosPadre.item(Constantes.MAGIC_NUMBER_0).getChildNodes().item(i).getChildNodes();
				int numElementos = nodosPadre.item(Constantes.MAGIC_NUMBER_0).getChildNodes().item(i).getChildNodes()
						.getLength();

				String valorName = "";
				String strValorName = "";
				for (int j = 0; j < numElementos; j++) { // me vienen X
															// elementos

					Node nodoValorName = nodos2.item(j);
					strValorName = nodos2.item(j).getNodeName();
					if (nodoValorName.getFirstChild() != null) {
						valorName = nodoValorName.getFirstChild().getNodeValue();
					}
					myHash.put(strValorName, valorName.toString().trim());
				}

				String taskid = "";
				String tipology = "";
				String messageGUID = "";

				tipology = (String) myHash.get(Constantes.CAT_TIPOLOGY);
				taskid = (String) myHash.get(Constantes.TASKID);
				messageGUID = (String) myHash.get(Constantes.MESG_CUID);
				if (Constantes.TIPOLOGY_TRA.equalsIgnoreCase(tipology.toUpperCase())) {
					if (taskid.equalsIgnoreCase(taskIDColas)) {
						myHash.clear();
						resultadoGuid = messageGUID;
						break;
					}
				}
			}
		}
		return resultadoGuid;
	}

	/**
	 * Funcion Convierte String a Document
	 * 
	 * @param textoXML
	 *            String
	 * 
	 * @return Document
	 * 
	 * @throws Exception
	 *             Exception
	 */

	public static Document stringToDocument(String textoXML) throws Exception {

		Document document = null;

		try {

			DocumentBuilderFactory factory = null;
			DocumentBuilder builder = null;

			factory = DocumentBuilderFactory.newInstance();
			factory.setValidating(false);
			factory.setIgnoringElementContentWhitespace(true);
			builder = factory.newDocumentBuilder();
			StringReader stringIn = new StringReader(textoXML);
			document = builder.parse(new InputSource(stringIn));

			// crear y añadir la pi

			ProcessingInstruction pi = document.createProcessingInstruction("xml",
					"version=\"1.0\" encoding=\"ISO-8859-1\"");
			document.appendChild(pi);

		} catch (Exception ex) {
			throw new Exception("Excepcion en stringToDocument:", ex);
		}

		return document;

	}

}
