package com.ejie.ab39a.utils;

import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;

import javax.servlet.http.HttpServletRequest;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.xpath.XPathExpressionException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import com.ejie.ab39a.constantes.Constantes;
import com.ejie.ab39a.seguridad.FeaDatosPersona;
import com.ejie.ab39a.seguridad.FeaDatosTarjeta;
import com.ejie.ab39a.seguridad.FeaDatosUsuarioJuegoBarcos;
import com.ejie.ab39a.seguridad.UsuarioConectado;
import com.ejie.x38.util.XmlManager;

import n38c.exe.N38API;

/**
 * 
 *  
 * 
 */
public class FeaHelper {

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

	private static final String XML_PROLOGO = "<?xml version=".concat("\"").concat("1.0\" encoding=")
			.concat("\" ISO-8859-1").concat("\" standalone=\"no\"?>");

	private static final String IZP_INICIO = "<izp:";
	private static final String IZP_FIN = "</izp:";

	private static final String NOMBRE_ELEMENTO_RUTA_XML_OFICNA = "rutaXMLJuegoBarcos";
	private static final String NOMBRE_ELEMENTO_RUTA_XML2_OFICNA = "rutaXMLCertGV";

	private static final String CODERROR_NOERROR = "0";

	// private static final String XPATH_N38B64THIRDPARTYASSERTION =
	// "/n38/elementos/elemento[@subtipo='N38Sesion']/parametro[@id='n38b64thirdpartyassertion']/valor";
	private static final String XPATH_N38B64THIRDPARTYASSERTION = "//parametro[@id='n38b64thirdpartyassertion']/valor";
	private static final String XPATH_RESULTADO_CODERROR = "/Envelope/Body/IdentificacionResponse/resultado/codigoError";
	private static final String XPATH_RESULTADO_DES_ERROR_CAS = "/Envelope/Body/IdentificacionResponse/resultado/descripcionError";
	private static final String XPATH_RESULTADO_DES_ERROR_EUS = "/Envelope/Body/IdentificacionResponse/resultado/descripcionErrorEU";

	private static final String XPATH_DATOSPERSONA_NOMBRE = "/Envelope/Body/IdentificacionResponse/datosPersona/nombre";
	private static final String XPATH_DATOSPERSONA_DNI = "/Envelope/Body/IdentificacionResponse/datosPersona/DNI";
	private static final String XPATH_DATOSPERSONA_PRIMERAPELLIDO = "/Envelope/Body/IdentificacionResponse/datosPersona/primerApellido";
	private static final String XPATH_DATOSPERSONA_SEGUNDOAPELLIDO = "/Envelope/Body/IdentificacionResponse/datosPersona/segundoApellido";
	private static final String XPATH_DATOSPERSONA_FECHANACIMENTO = "/Envelope/Body/IdentificacionResponse/datosPersona/fechaNacimento";

	private static final String XPATH_DATOSTARJETA_NUMREFERENCIA_TARJETA = "/Envelope/Body/IdentificacionResponse/datosJuegoBarcos/numeroRefJuegoBarcos";
	private static final String XPATH_DATOSTARJETA_ENTIDAD_EMISORA_TARJETA = "/Envelope/Body/IdentificacionResponse/datosJuegoBarcos/entidadEmisora";
	private static final String XPATH_DATOSTARJETA_ENTIDAD_TRAMITADORA_TARJETA = "/Envelope/Body/IdentificacionResponse/datosJuegoBarcos/entidadTramitadora";

	// private static final String XPATH_DATOSPERSONA_CIF_GV =
	// "/Envelope/Body/VerifyResponse/OptionalOutputs/CertificateValues/CertificateList/certificate/certificado/suscriptor/cif";
	// private static final String XPATH_DATOSPERSONA_DNI_GV =
	// "/Envelope/Body/VerifyResponse/OptionalOutputs/CertificateValues/CertificateList/certificate/certificado/suscriptor/dni";

	/**
	 * 
	 */
	private FeaHelper() {
		super();
	}

	/**
	 * get Datos Juego Barcos FromFea
	 * 
	 * @param request
	 *            HttpServletRequest request
	 * @param usuario
	 *            UsuarioConectado usuario
	 * 
	 * @return FeaDatosUsuarioJuegoBarcos objeto con los datos del juego de
	 *         barcos: persona + tarjeta
	 * @throws Exception
	 *             e e
	 */
	// CHECKSTYLE:OFF
	public static FeaDatosUsuarioJuegoBarcos getDatosJuegoBarcosFromFea(HttpServletRequest request,
			UsuarioConectado usuario) throws Exception {

		FeaHelper.logger.debug("getDatosJuegoBarcosFromFea() - Entrando");

		Document feaDocument;

		String simulacion = UtilProperties.Props.en_oficina.getValue();
		if (simulacion != null && Boolean.valueOf(simulacion)) {
			// en Oficina, leer XML de sesion
			feaDocument = FeaHelper.obtenerJuegoBarcosOficina(request, usuario);
		} else {
			feaDocument = FeaHelper.getFeaDocument(request);
		}

		//FeaHelper.logger.debug("n38b64thirdpartyassertion : ".concat(FeaHelper.getStringFromDocument(feaDocument)));

		String codError = FeaHelper.getDocumentParam(feaDocument, FeaHelper.XPATH_RESULTADO_CODERROR);
		String strDesCasError = FeaHelper.getDocumentParam(feaDocument, FeaHelper.XPATH_RESULTADO_DES_ERROR_CAS);
		String strDesEusError = FeaHelper.getDocumentParam(feaDocument, FeaHelper.XPATH_RESULTADO_DES_ERROR_EUS);

		if (!FeaHelper.CODERROR_NOERROR.equals(codError)) {
			// se ha producido un error
			StringBuilder str = new StringBuilder(Constantes.STRING_BUILDER_INIT);
			str.append("codError:").append(codError);
			str.append("-descripcion cas:").append(strDesCasError);
			str.append("-descripcion eus:").append(strDesEusError);

			FeaHelper.logger.debug("getDatosJuegoBarcosFromFea() - ERROR en Juego barcos:".concat(str.toString()));
			throw new Exception(str.toString());
		}

		FeaDatosUsuarioJuegoBarcos datosJB = new FeaDatosUsuarioJuegoBarcos();

		String dni = FeaHelper.getDocumentParam(feaDocument, FeaHelper.XPATH_DATOSPERSONA_DNI);
		String nombre = FeaHelper.getDocumentParam(feaDocument, FeaHelper.XPATH_DATOSPERSONA_NOMBRE);
		String primerApellido = FeaHelper.getDocumentParam(feaDocument, FeaHelper.XPATH_DATOSPERSONA_PRIMERAPELLIDO);
		String segundoApellido = FeaHelper.getDocumentParam(feaDocument, FeaHelper.XPATH_DATOSPERSONA_SEGUNDOAPELLIDO);
		String fechaNacimiento = FeaHelper.getDocumentParam(feaDocument, FeaHelper.XPATH_DATOSPERSONA_FECHANACIMENTO);

		String numeroRefTarjeta = FeaHelper.getDocumentParam(feaDocument,
				FeaHelper.XPATH_DATOSTARJETA_NUMREFERENCIA_TARJETA);
		String entidadEmisoraTarjeta = FeaHelper.getDocumentParam(feaDocument,
				FeaHelper.XPATH_DATOSTARJETA_ENTIDAD_EMISORA_TARJETA);
		String entidadTramitadoraTarjeta = FeaHelper.getDocumentParam(feaDocument,
				FeaHelper.XPATH_DATOSTARJETA_ENTIDAD_TRAMITADORA_TARJETA);

		FeaHelper.logger.debug("dni : ".concat(dni == null ? "" : dni));
		FeaHelper.logger.debug("nombre : ".concat(nombre == null ? "" : nombre));
		FeaHelper.logger.debug("primerApellido : ".concat(primerApellido == null ? "" : primerApellido));
		FeaHelper.logger.debug("segundoApellido : ".concat(segundoApellido == null ? "" : segundoApellido));
		FeaHelper.logger.debug("fechaNacimiento : ".concat(fechaNacimiento == null ? "" : fechaNacimiento));

		FeaHelper.logger.debug("numeroRefTarjeta:".concat(numeroRefTarjeta == null ? "" : numeroRefTarjeta));
		FeaHelper.logger
				.debug("entidadEmisoraTarjeta : ".concat(entidadEmisoraTarjeta == null ? "" : entidadEmisoraTarjeta));
		FeaHelper.logger.debug("entidadTramitadoraTarjeta : "
				.concat((entidadTramitadoraTarjeta == null ? "" : entidadTramitadoraTarjeta)));

		datosJB.setDatosPersonaJuegoBarcos(
				new FeaDatosPersona(dni, nombre, primerApellido, segundoApellido, fechaNacimiento));

		datosJB.setDatosTarjetaJuegoBarcos(
				new FeaDatosTarjeta(numeroRefTarjeta, entidadEmisoraTarjeta, entidadTramitadoraTarjeta));

		FeaHelper.logger.debug("getDatosJuegoBarcosFromFea() - Saliendo");
		return datosJB;

	}

	/**
	 * get Datos Juego Barcos FromFea
	 * 
	 * @param request
	 *            HttpServletRequest request
	 * @param usuario
	 *            UsuarioConectado usuario
	 * 
	 * @return FeaDatosUsuarioJuegoBarcos objeto con los datos del juego de
	 *         barcos: persona + tarjeta
	 * @throws Exception
	 *             e e
	 */
	// CHECKSTYLE:OFF
	public static FeaDatosUsuarioJuegoBarcos getDatosCertGVsFromFea(HttpServletRequest request,
			UsuarioConectado usuario) throws Exception {

		// final String XPATH_DNI = "//izp:dni";
		// final String XPATH_CIF = "//izp:cif";
		final String XPATH_DNI = "//dni";
		final String XPATH_CIF = "//cif";
		// final String XPATH_NOMBRE = "//izp:nombre/text()";
		// final String XPATH_NOMBRECOMPLETO = "//izp:nombreCompleto/text()";
		// final String XPATH_RAZON = "//izp:razonsocial/text()";
		// final String XPATH_APE1 = "//izp:apellido1/text()";
		// final String XPATH_APE2 = "//izp:apellido2/text()";
		// final String XPATH_APELLIDOS = "//izp:apellidos/text()";

		FeaHelper.logger.debug("getDatosCertGVsFromFea() - Entrando");

		Document feaDocument = null;

		String simulacion = UtilProperties.Props.en_oficina.getValue();
		if (simulacion != null && Boolean.valueOf(simulacion)) {
			// en Oficina, leer XML de sesion
			FeaHelper.logger.debug("getDatosCertGVsFromFea() - por oficina");
			feaDocument = FeaHelper.obtenerCertGVOficina(request, usuario);
		} else {
			FeaHelper.logger.debug("getDatosCertGVsFromFea() - NO por oficina");
			feaDocument = FeaHelper.getFeaDocument(request);
		}

		// String documentoString =
		// FeaHelper.getStringFromDocument(feaDocument);

		String documentoString = UtilXML.parseDocument2String(feaDocument);

		//FeaHelper.logger.debug("n38b64thirdpartyassertion : ".concat((documentoString == null) ? "" : documentoString));

		FeaDatosUsuarioJuegoBarcos datosJB = new FeaDatosUsuarioJuegoBarcos();

		FeaHelper.logger.debug("getDatosCertGVsFromFea : obteniendo dni ");

		String dni = null;
		String cif = null;

		if (FeaHelper.getDocumentNode(feaDocument, XPATH_DNI)) {
			dni = FeaHelper.getDocumentParam(feaDocument, XPATH_DNI);
		} else {
			FeaHelper.logger.debug("getDatosCertGVsFromFea : no tiene dni ");
		}

		// String dni = FeaHelper.getDocumentParam(documentoString, XPATH_DNI);
		FeaHelper.logger.debug("getDatosCertGVsFromFea : obteniendo CIF ");
		if (FeaHelper.getDocumentNode(feaDocument, XPATH_CIF)) {
			cif = FeaHelper.getDocumentParam(feaDocument, XPATH_CIF);
		} else {
			FeaHelper.logger.debug("getDatosCertGVsFromFea : no tiene CIF ");
		}

		// String cif = FeaHelper.getDocumentParam(documentoString, XPATH_CIF);

		FeaHelper.logger.debug("dni : ".concat(dni == null ? "" : dni));
		FeaHelper.logger.debug("cif : ".concat(cif == null ? "" : cif));

		datosJB.setDatosPersonaJuegoBarcos(new FeaDatosPersona(dni, cif));

		FeaHelper.logger.debug("getDatosCertGVsFromFea() - Saliendo");
		return datosJB;

	}
	// CHECKSTYLE:ON

	/**
	 * 
	 * @param request
	 *            HttpServletRequest request
	 * @return Document Document
	 * @throws Exception
	 *             e e
	 */
	private static Document getFeaDocument(HttpServletRequest request) throws Exception {

		// Se instancia N38API
		N38API n38api = new N38API(request);
		// Se recupera el xml de sesión
		Document n38ItemSesion = n38api.n38ItemSesion();
		// Se recupera el parametro n38b64thirdpartyassertion del xml de sesión
		String n38b64thirdpartyassertion = FeaHelper.getDocumentParam(n38ItemSesion,
				FeaHelper.XPATH_N38B64THIRDPARTYASSERTION);

		String decodedParam = null;
		Document documentFromString = null;
		try {
			decodedParam = Utilidades.decodeBase64(n38b64thirdpartyassertion);

			if (decodedParam != null && decodedParam.indexOf("<?xml") > -1) {
				decodedParam = decodedParam.replaceAll(FeaHelper.XML_PROLOGO, "");
			}

			if (decodedParam != null && decodedParam.indexOf(FeaHelper.IZP_INICIO) > -1) {
				decodedParam = decodedParam.replaceAll(FeaHelper.IZP_INICIO, "<");
				decodedParam = decodedParam.replaceAll(FeaHelper.IZP_FIN, "</");
			}

			// Se construye un Document a partir del string decodificado
			documentFromString = FeaHelper.getDocumentFromString(decodedParam);

			// Se retorna el documento
			return documentFromString;
		} catch (Exception e) {
			FeaHelper.logger.debug("ERROR:getFeaDocument,".concat(e.getMessage()));
			FeaHelper.logger.error("ERROR:getFeaDocument:", e);
			throw e;
		}
	}

	/**
	 * Devuelve si existe el nodo especificado dentro del Documento
	 * 
	 * @param document
	 *            Document
	 * @param xpath
	 *            String
	 * @return boolean
	 */
	private static boolean getDocumentNode(Document document, String xpath) {
		boolean result = false;
		try {
			Node searchDomNode = XmlManager.searchDomNode(document, xpath);
			if (searchDomNode != null) {
				result = true;
			}

		} catch (TransformerException e) {
			result = false;
		}
		return result;
	}

	/**
	 * 
	 * @param document
	 *            Document document
	 * @param xpath
	 *            String xpath
	 * @return String String
	 */
	private static String getDocumentParam(Document document, String xpath) {
		try {
			Node searchDomNode = XmlManager.searchDomNode(document, xpath);
			String str = "";
			if (searchDomNode != null) {
				str = searchDomNode.getTextContent();
			}
			return str;
		} catch (TransformerException e) {

			FeaHelper.logger.debug("getDocumentParam:ERROR:".concat(e.getMessage()));
			FeaHelper.logger.error("getDocumentParam:ERROR:", e);
			throw new SecurityException(e);
		}
	}

	/**
	 * 
	 * @param document
	 *            String document
	 * @param xpath
	 *            String xpath
	 * @return String String
	 * @throws IOException
	 *             e
	 * @throws SAXException
	 *             e
	 * @throws ParserConfigurationException
	 *             e
	 * @throws XPathExpressionException
	 *             e
	 * 
	 * @throws UnsupportedEncodingException
	 *             e
	 * 
	 */
	private static String getDocumentParam(String document, String xpath) throws UnsupportedEncodingException,
			XPathExpressionException, ParserConfigurationException, SAXException, IOException {
		try {

			XMLAccessor acc = new XMLAccessor(document);

			return acc.getValue(xpath);

		} catch (XPathExpressionException e) {

			FeaHelper.logger.debug("getDocumentParam:ERROR:".concat(e.getMessage()));
			FeaHelper.logger.error("getDocumentParam:ERROR:", e);
			throw e;
		}
	}

	/**
	 * 
	 * @param sourceString
	 *            String sourceString
	 * @return Document Document
	 */
	private static Document getDocumentFromString(String sourceString) {
		DocumentBuilder db;
		try {

			db = DocumentBuilderFactory.newInstance().newDocumentBuilder();

			InputSource is = new InputSource();
			is.setCharacterStream(new StringReader(sourceString));

			return db.parse(is);

		} catch (ParserConfigurationException e) {
			throw new SecurityException(e);
		} catch (SAXException e) {
			throw new SecurityException(e);
		} catch (IOException e) {
			throw new SecurityException(e);
		}
	}

	/**
	 * 
	 * @param doc
	 *            Document doc
	 * @return String String
	 */
	private static String getStringFromDocument(Document doc) {
		try {
			DOMSource domSource = new DOMSource(doc);
			StringWriter writer = new StringWriter();
			StreamResult result = new StreamResult(writer);
			TransformerFactory tf = TransformerFactory.newInstance();
			Transformer transformer = tf.newTransformer();
			transformer.transform(domSource, result);
			return writer.toString();
		} catch (TransformerException ex) {
			FeaHelper.logger.debug("getStringFromDocument() - ERROR ");
			return null;
		}
	}

	/**
	 * 
	 * Obtiene el XML de sesion que contiene la informacion de Juego de Barcos.
	 * SOLO PARA OFICINA
	 * 
	 * 
	 * @param request
	 *            HttpServletRequest request
	 * @param usuario
	 *            UsuarioConectado usuario
	 * @return Document XML de sesion
	 * @throws IOException
	 *             e e
	 */
	private static Document obtenerJuegoBarcosOficina(HttpServletRequest request, UsuarioConectado usuario)
			throws IOException {

		FeaHelper.logger.debug("obtenerJuegoBarcosOficina() - Entrando");

		if (!usuario.containsSubjectCert(FeaHelper.NOMBRE_ELEMENTO_RUTA_XML_OFICNA)) {
			FeaHelper.logger.debug("obtenerJuegoBarcosOficina() - ERROR no hay ruta al fichero de juego de barcos");
			throw new IOException("obtenerJuegoBarcosOficina() - ERROR no hay ruta al fichero de juego de barcos");
		}

		String rutafisica = usuario.getSubjectCert(FeaHelper.NOMBRE_ELEMENTO_RUTA_XML_OFICNA);
		FeaHelper.logger.debug("obtenerJuegoBarcosOficina() - ruta es:".concat((rutafisica == null ? "" : rutafisica)));

		FeaHelper.logger.debug("obtenerJuegoBarcosOficina() - Saliendo");

		Document n38ItemSesion = FeaHelper.getDocumentFromString(Utilidades.getContenidoFichero(rutafisica));
		// Se recupera el parametro n38b64thirdpartyassertion del xml de sesión
		String n38b64thirdpartyassertion = FeaHelper.getDocumentParam(n38ItemSesion,
				FeaHelper.XPATH_N38B64THIRDPARTYASSERTION);
		// El parametro está en Base64, de decodifica
		String decodedParam = null;
		Document documentFromString = null;
		try {
			decodedParam = Utilidades.decodeBase64(n38b64thirdpartyassertion);
			// Se construye un Document a partir del string decodificado
			documentFromString = FeaHelper.getDocumentFromString(decodedParam);
			// Se retorna el documento
			return documentFromString;

		} catch (IOException e) {
			FeaHelper.logger.debug("obtenerJuegoBarcosOficina:ERROR:".concat(e.getMessage()));
			FeaHelper.logger.error("obtenerJuegoBarcosOficina:ERROR:", e);
			throw e;
		}
	}

	/**
	 * 
	 * Obtiene el XML de sesion que contiene la informacion de Juego de Barcos.
	 * SOLO PARA OFICINA
	 * 
	 * 
	 * @param request
	 *            HttpServletRequest request
	 * @param usuario
	 *            UsuarioConectado usuario
	 * @return Document XML de sesion
	 * @throws IOException
	 *             e e
	 */
	private static Document obtenerCertGVOficina(HttpServletRequest request, UsuarioConectado usuario)
			throws IOException {

		FeaHelper.logger.debug("obtenerJuegoBarcosOficina() - Entrando");

		if (!usuario.containsSubjectCert(FeaHelper.NOMBRE_ELEMENTO_RUTA_XML2_OFICNA)) {
			FeaHelper.logger.debug("obtenerJuegoBarcosOficina() - ERROR no hay ruta al fichero de juego de barcos");
			throw new IOException("obtenerJuegoBarcosOficina() - ERROR no hay ruta al fichero de juego de barcos");
		}

		String rutafisica = usuario.getSubjectCert(FeaHelper.NOMBRE_ELEMENTO_RUTA_XML2_OFICNA);
		FeaHelper.logger.debug("obtenerJuegoBarcosOficina() - ruta es:".concat((rutafisica == null ? "" : rutafisica)));

		FeaHelper.logger.debug("obtenerJuegoBarcosOficina() - Saliendo");

		Document n38ItemSesion = FeaHelper.getDocumentFromString(Utilidades.getContenidoFichero(rutafisica));
		// Se recupera el parametro n38b64thirdpartyassertion del xml de sesión
		String n38b64thirdpartyassertion = FeaHelper.getDocumentParam(n38ItemSesion,
				FeaHelper.XPATH_N38B64THIRDPARTYASSERTION);
		// El parametro está en Base64, de decodifica
		String decodedParam = null;
		Document documentFromString = null;
		try {
			decodedParam = Utilidades.decodeBase64(n38b64thirdpartyassertion);

			if (decodedParam != null && decodedParam.indexOf("<?xml") > -1) {
				decodedParam = decodedParam.replaceAll(FeaHelper.XML_PROLOGO, "");
			}

			if (decodedParam != null && decodedParam.indexOf(FeaHelper.IZP_INICIO) > -1) {
				decodedParam = decodedParam.replaceAll(FeaHelper.IZP_INICIO, "<");
				decodedParam = decodedParam.replaceAll(FeaHelper.IZP_FIN, "</");
			}

			// Se construye un Document a partir del string decodificado
			documentFromString = FeaHelper.getDocumentFromString(decodedParam);
			// Se retorna el documento
			return documentFromString;

		} catch (IOException e) {
			FeaHelper.logger.debug("obtenerJuegoBarcosOficina:ERROR:".concat(e.getMessage()));
			FeaHelper.logger.error("obtenerJuegoBarcosOficina:ERROR:", e);
			throw e;
		}
	}
}