package com.ejie.ad74a.util.comun;

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.rmi.UnmarshalException;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.FactoryConfigurationError;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
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.apache.xml.serialize.OutputFormat;
//import org.apache.xml.serialize.XMLSerializer;
//import org.apache.xpath.XPathAPI;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import com.bea.common.security.utils.encoders.BASE64Decoder;
import com.bea.xml.XmlCursor;
import com.bea.xml.XmlObject;
import com.ejie.ad74a.constantes.Constantes;
import com.ejie.ad74a.ws.client.firmaX43fn.EjgvDocument;
import com.sun.org.apache.xpath.internal.XPathAPI;

import weblogic.apache.xml.serialize.OutputFormat;
import weblogic.apache.xml.serialize.XMLSerializer;

public class UtilidadesXML {
	/** Constructor privado */
	private UtilidadesXML() {
	}

	private static final Logger logger = LoggerFactory.getLogger(UtilidadesXML.class);

	/**
	 * quitarNameSpaces(com.bea.xml.XmlObject oXmlEntrada)
	 * 
	 * @param oXmlEntrada XmlObject
	 * @return XmlObject
	 * @exception Exception e
	 */
	public static XmlObject quitarNameSpaces(XmlObject oXmlEntrada) throws Exception {

		String sNuevoNameSpace;

		XmlObject oXmlRetorno = oXmlEntrada;
		XmlCursor oXmlCursor = oXmlRetorno.newCursor();

		oXmlCursor.toNextToken();

		while (oXmlCursor.hasNextToken()) {

			if (oXmlCursor.isNamespace()) {
				oXmlCursor.removeXml();
			} else {

				if (oXmlCursor.isStart()) {
					sNuevoNameSpace = oXmlCursor.getName().getLocalPart();
					oXmlCursor.setName(new javax.xml.namespace.QName(sNuevoNameSpace));
				}
				oXmlCursor.toNextToken();
			}
		}
		oXmlCursor.dispose();

		return oXmlRetorno;

	}

	/**
	 * quitarNameSpacesString(com.bea.xml.XmlObject oXmlEntrada)
	 * 
	 * @param oXmlEntrada XmlObject
	 * @return String
	 * @exception Exception e
	 */
	public static String quitarNameSpacesString(XmlObject oXmlEntrada) throws Exception {

		XmlObject xmlO = UtilidadesXML.quitarNameSpaces(oXmlEntrada);

		return (xmlO != null) ? xmlO.xmlText() : "";
	}

	/**
	 * quitarXMLFragment(String xmlFragment)
	 * 
	 * @param xmlFragment String
	 * @return String
	 * @exception Exception e
	 */
	public static String quitarXMLFragment(String xmlFragment) throws Exception {
		String xmlSinFragment = null;
		if (xmlFragment != null) {
			xmlSinFragment = xmlFragment.replaceAll("<xml-fragment>", "");
			xmlSinFragment = xmlSinFragment.replaceAll("</xml-fragment>", "");
		}
		return xmlSinFragment;
	}

	/**
	 * createLanguageEstructura(String etiqueta, String value)
	 * 
	 * @param etiqueta String
	 * @param value    String
	 * @return String
	 * @exception Exception e
	 */
	public static String createLanguageEstructura(String etiqueta, String value) throws Exception {
		StringBuffer languageEstructura = new StringBuffer();
		languageEstructura.append("<").append(etiqueta).append(">");
		languageEstructura.append("<language locale=\"es\">").append(value).append("</language>");
		languageEstructura.append("<language locale=\"eu\">").append(value).append("</language>");
		languageEstructura.append("</").append(etiqueta).append(">");
		return languageEstructura.toString();
	}

	/**
	 * createLanguageEstructura(String etiqueta, String value_es, String value_eu)
	 * 
	 * @param etiqueta String
	 * @param value_es String
	 * @param value_eu String
	 * @return String
	 * @exception Exception e
	 */
	public static String createLanguageEstructura(String etiqueta, String value_es, String value_eu) throws Exception {
		StringBuffer languageEstructura = new StringBuffer();
		languageEstructura.append("<").append(etiqueta).append(">");
		languageEstructura.append("<language locale=\"es\">").append(value_es).append("</language>");
		languageEstructura.append("<language locale=\"eu\">").append(value_eu).append("</language>");
		languageEstructura.append("</").append(etiqueta).append(">");
		return languageEstructura.toString();
	}

	/**
	 * Removes namespaces from string
	 * 
	 * @param xmlString String
	 * @return String
	 */
	public static String removeXmlStringNamespaceAndPreamble(String xmlString) {
		return xmlString.replaceAll("(<\\?[^<]*\\?>)?", ""). /* remove preamble */
				replaceAll("xmlns.*?(\"|\').*?(\"|\')", "") /* remove xmlns declaration */
				.replaceAll("(<)(\\w+:)(.*?>)", "$1$3") /* remove opening tag prefix */
				.replaceAll("(</)(\\w+:)(.*?>)", "$1$3"); /* remove closing tags prefix */
	}

	public static Document getDocumentFromString(String strInput) throws Exception {
		/**
		 * Variables para la generacion del Document a partir del String
		 */

		Document document = null;

		// Inizializacion del parser
		DocumentBuilderFactory docBFact = DocumentBuilderFactory.newInstance();
		DocumentBuilder documentBuilder = docBFact.newDocumentBuilder();

		/*
		 * Se parsea el xml segun encoding del xml Si no hay encoding se utilizara el de
		 * por defecto de la libreria -> UTF-8
		 */
		String encoding = "UTF-8";
		document = documentBuilder.parse((InputStream) new ByteArrayInputStream(strInput.getBytes(encoding)));

		return document;
	}

	public static String getStringFromDocument(Document doc) throws TransformerException {
		DOMSource domSource = new DOMSource(doc);
		StringWriter writer = new StringWriter();
		StreamResult result = new StreamResult(writer);
		TransformerFactory tf = TransformerFactory.newInstance();
		Transformer transformer = tf.newTransformer();
		transformer.setOutputProperty(OutputKeys.INDENT, "yes");
		transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
		transformer.transform(domSource, result);
		return writer.toString();
	}

	/**
	 * Convierte un Node en String
	 * 
	 * @param nodo Nodo que se quiere transformar en String
	 * @return El nodo convertido a String
	 * @throws Exception exception
	 */
	public static String getStringFromNode(Node nodo) throws Exception {
		String xmlString = "";
		TransformerFactory tFactory = TransformerFactory.newInstance();
		Transformer transformer;
		try {
			transformer = tFactory.newTransformer();
			DOMSource source = new DOMSource(nodo);
			StringWriter sw = new StringWriter();
			StreamResult result = new StreamResult(sw);
			try {
				transformer.transform(source, result);
			} catch (TransformerException e1) {
				throw new Exception(e1.getMessage());
			}
			xmlString = sw.toString();
		} catch (TransformerConfigurationException e1) {
			throw new Exception(e1.getMessage());
		}

		try {
			Document doc = getDocumentFromString(xmlString);
			// doc.appendChild(nodo);
			// Document doc = nodo.getOwnerDocument();
			OutputFormat format = new OutputFormat(doc);
			format.setIndent(Constantes.TRES);
			StringWriter stringOut = new StringWriter();
			XMLSerializer serial = new XMLSerializer(stringOut, format);
			serial.serialize(doc);
			xmlString = stringOut.toString();
		} catch (FactoryConfigurationError e) {
			throw new Exception(e.getMessage());
		} catch (ParserConfigurationException e) {
			throw new Exception(e.getMessage());
		} catch (IOException e) {
			throw new Exception(e.getMessage());
		} catch (Exception e) {
			throw new Exception(e.getMessage());
		}

		return xmlString;
	}

	/**
	 * Recupera el vector con los valores de los elementos parseados en un documento
	 * XML.
	 * 
	 * @param strPfDocument Documento que contiene el XML
	 * @param strPfXPath    Ruta de la etiqueta que se quiere parsear
	 * @return El nodo seleccionado por la ruta
	 * @throws Exception dom util exception
	 */
	public static Node retrieveNodeXPath(Document strPfDocument, String strPfXPath) throws Exception {
		try {
			return XPathAPI.selectSingleNode(strPfDocument, strPfXPath);

		} catch (TransformerException e) {
			throw new Exception(e.getMessage());
		}
	}

	/**
	 * 
	 * @param documentoCS
	 * @param xpath
	 * @return
	 * @throws Exception
	 */
	public static String[] obtenerValorNodosXPath(Document documentoCS, String xpath) throws Exception {
		String[] pathsFunciones = null;
		try {
			NodeList parametros = XPathAPI.selectNodeList(documentoCS, xpath);
			pathsFunciones = new String[parametros.getLength()];
			for (int i = 0; i < parametros.getLength(); i++) {
				String aux;
				Node paramPath = parametros.item(i);
				if (paramPath != null) {
					aux = paramPath.getNodeValue();
				} else {
					aux = "";
				}
				pathsFunciones[i] = aux;
			}
		} catch (Exception e) {
			throw e;
		}
		return pathsFunciones;
	}

	/**
	 * Removes &lt; y &gt;
	 * 
	 * @param xmlString String
	 * @return String
	 */
	public static String replaceXMLCharacters(String xmlString) {
		return xmlString.replaceAll("&lt;", "<").replaceAll("&gt;", ">");
	}

	/**
	 * Removes < y >
	 * 
	 * @param xmlString String
	 * @return String
	 */
	public static String unreplaceXMLCharacters(String xmlString) {
		return xmlString.replaceAll("<", "&lt;").replaceAll(">", "&gt;");
	}

	/**
	 * ejgvDocumentToString
	 * 
	 * @param ejgvDocumentType EjgvDocument
	 * @return String
	 * @throws Exception e / public static String ejgvDocumentToString(EjgvDocument
	 *                   ejgvDocumentType) throws Exception {
	 * 
	 *                   final JAXBContext jaxbContext =
	 *                   JAXBContext.newInstance(EjgvDocument.class); StringWriter
	 *                   writer = new StringWriter(); Marshaller m =
	 *                   jaxbContext.createMarshaller();
	 *                   m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT,
	 *                   Boolean.TRUE);
	 * 
	 *                   jaxbContext.createMarshaller().marshal(ejgvDocumentType,
	 *                   writer); return writer.toString();
	 * 
	 *                   }
	 */

	/**
	 * Dado un xml y los nomvres de las etiquetas que se quieren "limpir" devuelve
	 * un XML con las etiquetas "limpis", es decir, sin atributos
	 * 
	 * @param xml  el xml
	 * @param tags las etiquetas
	 * @return xml "limpio
	 */
	public static String cleanTags(String xml, String... tags) {
		String result = xml;
		for (String tag : tags) {
			result = result.replaceAll("<" + tag + ".*?>", "<" + tag + ">").replace(" xsi:nil=\"true\"", "");
		}
		return result;
	}

	/**
	 * Convierte un xml escapado con codigos html e un xml correctamente formateado
	 * 
	 * @param xml escapado
	 * @return xml correctamente formateado
	 */
	public static String htmlToXml(String xml) {
		return xml.replaceAll("&lt;", "<").replaceAll("&gt;", ">");
	}

	public static String marshalObjectWS(Object o) throws Exception {
		StringWriter sw = new StringWriter();
		JAXBContext jc = JAXBContext.newInstance(o.getClass());
		Marshaller marshaller = jc.createMarshaller();
		// para que no incluya <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
		marshaller.setProperty("com.sun.xml.bind.xmlDeclaration", false);
		marshaller.marshal(o, sw);
		return sw.toString();
	}

	/**
	 * 
	 * @param node Document
	 * @return String
	 * @deprecated
	 */
	public static String parseDocument2String_(Document node) {
		UtilidadesXML.logger.debug("INI parseDocumen2String");
		try {
			Source source = new DOMSource(node);
			StringWriter stringWriter = new StringWriter();
			Result result = new StreamResult(stringWriter);
			TransformerFactory factory = TransformerFactory.newInstance();
			Transformer transformer = factory.newTransformer();
			transformer.transform(source, result);
			String resultado = stringWriter.getBuffer().toString();
			UtilidadesXML.logger.debug("FIN parseDocument2String");
			return resultado;
		} catch (TransformerConfigurationException e) {
			UtilidadesXML.logger.error("Se ha producido un error en el metodo parseDocumen2String: " + e.getMessage());
		} catch (TransformerException e) {
			UtilidadesXML.logger.error("Se ha producido un error en el metodo parseDocumen2String: " + e.getMessage());
		}
		return null;
	}

	public static String parseDocument2String(Document doc) {
		StringWriter sw;
		try {
			TransformerFactory tFactory = TransformerFactory.newInstance();
			Transformer transformer = tFactory.newTransformer();
			DOMSource source = new DOMSource(doc);
			sw = new StringWriter();
			StreamResult result = new StreamResult(sw);
			transformer.transform(source, result);
			return sw.toString();
		} catch (TransformerConfigurationException e) {
			UtilidadesXML.logger.error("Se ha producido un error en el metodo parseDocumen2String: " + e.getMessage());
		} catch (TransformerException e) {
			UtilidadesXML.logger.error("Se ha producido un error en el metodo parseDocumen2String: " + e.getMessage());
		}
		return null;
	}

	/**
	 * Obtiene un Document a partir de una cadena
	 * 
	 * @param s la cadena
	 * @return Document el document
	 */
	public static Document string2Document(String s) {
		UtilidadesXML.logger.debug("INI string2Document");
		Document tmpX = null;
		DocumentBuilder builder = null;

		try {
			builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
			tmpX = builder.parse(new ByteArrayInputStream(s.getBytes()));
		} catch (Exception e) {
			UtilidadesXML.logger.error("Error al transformar el String a Document", e);
		}
		return tmpX;

	}

	public static Object unmarshallerObject(String xml, Class clazz) throws UnmarshalException {
		try {
			JAXBContext jaxbContext;
			jaxbContext = JAXBContext.newInstance(clazz);
			Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();

			return jaxbUnmarshaller.unmarshal(new ByteArrayInputStream(xml.getBytes("UTF-8")));
		} catch (JAXBException e) {
			throw new UnmarshalException("Error de JAXB al traducir el XML", e);
		} catch (UnsupportedEncodingException e) {
			throw new UnmarshalException("Error de codificacion al leer el XML", e);
		}
	}

	/**
	 * Decodifica una cadena de caracteres que se encuentra codificada en formato
	 * Base64
	 * 
	 * @param encData Datos a decodificar
	 * @return Bytes con los datos decodificados
	 * @throws Exception e
	 */
	public static byte[] decode(String encData) throws Exception {
		byte[] decData = null;
		BASE64Decoder decoder;
		decoder = new BASE64Decoder();

		decData = decoder.decodeBuffer(encData);

		return decData;
	}

	/**
	 * ejgvDocumentToString
	 * 
	 * @param ejgvDocumentType EjgvDocument
	 * @return String
	 * @throws Exception e
	 */
	public static EjgvDocument stringToejgvDocument(String firma) throws JAXBException {

		final JAXBContext jaxbContext = JAXBContext.newInstance(EjgvDocument.class);
		Marshaller m = jaxbContext.createMarshaller();
		m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);

		if (!firma.contains("http://com.ejie.x43f/X43FNSHF/")) {
			firma = firma.replace("<ejgvDocument>", "<ns2:ejgvDocument xmlns:ns2='http://com.ejie.x43f/X43FNSHF/'>");
			firma = firma.replace("</ejgvDocument>", "</ns2:ejgvDocument>");
		}

		EjgvDocument ejgv = (EjgvDocument) jaxbContext.createUnmarshaller().unmarshal(new StringReader(firma));
		return ejgv;
	}

}