/**
 * 
 */
package com.ejie.ad74a.ws.client.televida.service.impl;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;

import javax.xml.namespace.QName;
import javax.xml.soap.MessageFactory;
import javax.xml.soap.MimeHeaders;
import javax.xml.soap.SOAPBody;
import javax.xml.soap.SOAPBodyElement;
import javax.xml.soap.SOAPConnection;
import javax.xml.soap.SOAPConnectionFactory;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPEnvelope;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPHeader;
import javax.xml.soap.SOAPMessage;
import javax.xml.soap.SOAPPart;

import org.springframework.stereotype.Service;
import org.w3c.dom.Document;
import org.w3c.dom.DocumentFragment;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import com.ejie.ad74a.util.UtilProperties;
import com.ejie.ad74a.util.UtilTokenXlnets;
import com.ejie.ad74a.util.UtilidadesXML;
import com.ejie.ad74a.util.constantes.ConstantesBatch;
import com.ejie.ad74a.ws.client.televida.model.CabeceraType;
import com.ejie.ad74a.ws.client.televida.model.Comunicacion;
import com.ejie.ad74a.ws.client.televida.model.DatosBancariosType;
import com.ejie.ad74a.ws.client.televida.model.DatosDireccionType;
import com.ejie.ad74a.ws.client.televida.model.DatosEmisionType;
import com.ejie.ad74a.ws.client.televida.model.DatosIdentificacionType;
import com.ejie.ad74a.ws.client.televida.model.RespuestaType;
import com.ejie.ad74a.ws.client.televida.service.LiquidacionWS;

/**
 * Implementacion de la capa de servicio para el uso del WS
 * @author BICUGUAL
 *
 */
@Service
public class LiquidacionWSImpl implements LiquidacionWS{

	@Override
	public RespuestaType emitirLiquidacion(Comunicacion comunicacion) throws Exception {

		MessageFactory mfactory = MessageFactory.newInstance();
		SOAPMessage message = mfactory.createMessage();
		message.setProperty(SOAPMessage.CHARACTER_SET_ENCODING, "utf-8");

		SOAPPart soapPart = message.getSOAPPart();
		SOAPEnvelope envelope = soapPart.getEnvelope();
		envelope.setPrefix("soapenv");
		envelope.removeNamespaceDeclaration("SOAP-ENV");
		envelope.addNamespaceDeclaration("com", ConstantesBatch.NAMESPACE_AE01B);
		envelope.addNamespaceDeclaration("ns", "com/ejie/ae01b/xml");

		SOAPHeader header = envelope.getHeader();
		header.setPrefix("soapenv");
		addDocumentToSoapHeader(UtilTokenXlnets.getTokenDocumentXLNets("AD74A"), message);

		SOAPBody body = envelope.getBody();
		body.setPrefix("soapenv");
		
		SOAPBodyElement bodyElement = body.addBodyElement(envelope.createName("emitirLiquidacion", "com", ""));

		// DATOS CABECERA
		//El prefijo ns se aņade al final
		SOAPElement cabecera = bodyElement
				.addChildElement(new QName("cabecera"));

		if (null != comunicacion.getCabecera()) {
			this.addCabecera(comunicacion.getCabecera(), cabecera);
		}

		// DATOS EMISION
		//El prefijo ns se aņade al final		
		SOAPElement datosEmision = bodyElement
				.addChildElement(new QName("datosEmision"));
		
		if (null != comunicacion.getDatosEmision()) {
			this.addDatosEmision(comunicacion.getDatosEmision(), datosEmision);

			SOAPElement datosIdent = datosEmision.addChildElement(new QName("datosIdentificacion"));
			if (null != comunicacion.getDatosEmision().getDatosIdentificacion()) {
				this.addDatosIdentificacion(comunicacion.getDatosEmision().getDatosIdentificacion(), datosIdent);
			}

			SOAPElement datosDirec = datosEmision.addChildElement(new QName("datosDireccion"));
			if (null != comunicacion.getDatosEmision().getDatosDireccion()) {
				this.addDatosDireccion(comunicacion.getDatosEmision().getDatosDireccion(), datosDirec);
			}

			SOAPElement datosBanc = datosEmision.addChildElement(new QName("datosBancarios"));
			if (null != comunicacion.getDatosEmision().getDatosBancarios()) {
				this.addDatosBancarios(comunicacion.getDatosEmision().getDatosBancarios(), datosBanc);
			}
		}

		SOAPConnectionFactory factory = SOAPConnectionFactory.newInstance();
		SOAPConnection con = factory.createConnection();

		// imprimir request
		String xmlString = UtilidadesXML.printFormattedXML(message);
		
		MessageFactory factoryAux = MessageFactory.newInstance();
		InputStream is = new ByteArrayInputStream(StandardCharsets.UTF_8.encode(xmlString).array());
	    SOAPMessage soapMessage = factoryAux.createMessage(new MimeHeaders(), is);

		String url = UtilProperties.Props.URL_WS_LIQUIDACIONES.val();
		SOAPMessage responseWebServ = con.call(soapMessage, url);

		// imprimir response
		UtilidadesXML.printFormattedXML(responseWebServ);

		RespuestaType respuesta = this.soapMessageToRespuestaType(responseWebServ);
		
		con.close();

		return respuesta;

	}

	/**
	 * Incluye el documento en la cabecera del mensaje
	 * 
	 * @param document
	 * @param soapMessage
	 * @throws SOAPException
	 */
	private void addDocumentToSoapHeader(Document document, SOAPMessage soapMessage) throws SOAPException {

		DocumentFragment docFrag = document.createDocumentFragment();
		Element rootElement = document.getDocumentElement();
		if (rootElement != null) {
			docFrag.appendChild(rootElement);
			Document ownerDoc = soapMessage.getSOAPHeader().getOwnerDocument();
			Node replacingNode = ownerDoc.importNode(docFrag, true);
			soapMessage.getSOAPHeader().appendChild(replacingNode);

		}
	}

	
	/**
	 * Crea el apartado de datos de cabecera en el SOAPElement
	 * @param cab
	 * @param cabeceraSE
	 * @throws SOAPException
	 */
	private void addCabecera(CabeceraType cab, SOAPElement cabeceraSE) throws SOAPException {
		if (null != cab.getTipoOperacion())
			cabeceraSE.addChildElement(new QName("tipoOperacion")).addTextNode(cab.getTipoOperacion().value());
		if (null != cab.getUsuario())
			cabeceraSE.addChildElement(new QName("usuario")).addTextNode(cab.getUsuario());
		if (null != cab.getFComunicacion())
			cabeceraSE.addChildElement(new QName("fComunicacion")).addTextNode(cab.getFComunicacion());
	}

	/**
	 * Crea el apartado de datos de datos emision en el SOAPElement
	 * @param dEmision
	 * @param datosEmisionSE
	 * @throws SOAPException
	 */
	private void addDatosEmision(DatosEmisionType dEmision, SOAPElement datosEmisionSE) throws SOAPException {
		if (null != dEmision.getTipoIngreso())
			datosEmisionSE.addChildElement(new QName("tipoIngreso")).addTextNode(dEmision.getTipoIngreso());
		if (null != dEmision.getOrigen())
			datosEmisionSE.addChildElement(new QName("origen")).addTextNode(dEmision.getOrigen());
		if (null != dEmision.getEjercicio())
			datosEmisionSE.addChildElement(new QName("ejercicio")).addTextNode(dEmision.getEjercicio().toString());

		SOAPElement conceptos = datosEmisionSE.addChildElement(new QName("conceptos"));
		if (null != dEmision.getConceptos()) {
			SOAPElement concepto = conceptos.addChildElement(new QName("concepto"));

			if (!dEmision.getConceptos().getConcepto().isEmpty()) {
				concepto.addChildElement(new QName("idConcepto"))
						.addTextNode(String.valueOf(dEmision.getConceptos().getConcepto().get(0).getIdConcepto()));
				concepto.addChildElement(new QName("importe"))
						.addTextNode(String.valueOf(dEmision.getConceptos().getConcepto().get(0).getImporte()));
				if (null != dEmision.getConceptos().getConcepto().get(0).getCodSubv())
					concepto.addChildElement(new QName("codSubv"))
							.addTextNode(dEmision.getConceptos().getConcepto().get(0).getCodSubv());
				if (null != dEmision.getConceptos().getConcepto().get(0).getNumDocFinan())
					concepto.addChildElement(new QName("numDocFinan"))
							.addTextNode(dEmision.getConceptos().getConcepto().get(0).getNumDocFinan());
				if (null != dEmision.getConceptos().getConcepto().get(0).getCodCausa())
					concepto.addChildElement(new QName("codCausa"))
							.addTextNode(dEmision.getConceptos().getConcepto().get(0).getCodCausa());
				if (null != dEmision.getConceptos().getConcepto().get(0).getFReintegro())
					concepto.addChildElement(new QName("fReintegro"))
							.addTextNode(dEmision.getConceptos().getConcepto().get(0).getFReintegro());
			}
		}

		if (null != dEmision.getFLimitePago())
			datosEmisionSE.addChildElement(new QName("fLimitePago")).addTextNode(dEmision.getFLimitePago());

		if (null != dEmision.getExpediente())
			datosEmisionSE.addChildElement(new QName("expediente")).addTextNode(dEmision.getExpediente());

		if (null != dEmision.getConcepto())
			datosEmisionSE.addChildElement(new QName("concepto")).addTextNode(dEmision.getConcepto());

		datosEmisionSE.addChildElement(new QName("territorio")).addTextNode(String.valueOf(dEmision.getTerritorio()));
	}

	/**
	 * Crea el apartado de datos de identificacion en el SOAPElement
	 * 
	 * @param dIdent
	 * @param datosIdentificacionSE
	 * @throws SOAPException
	 */
	private void addDatosIdentificacion(DatosIdentificacionType dIdent, SOAPElement datosIdentificacionSE)
			throws SOAPException {
		datosIdentificacionSE.addChildElement(new QName("tipoIdentificador"))
				.addTextNode(String.valueOf(dIdent.getTipoIdentificador()));
		if (null != dIdent.getIdentificador())
			datosIdentificacionSE.addChildElement(new QName("identificador")).addTextNode(dIdent.getIdentificador());
		if (null != dIdent.getNombre())
			datosIdentificacionSE.addChildElement(new QName("nombre")).addTextNode(dIdent.getNombre());
		if (null != dIdent.getApellido1())
			datosIdentificacionSE.addChildElement(new QName("apellido1")).addTextNode(dIdent.getApellido1());
		if (null != dIdent.getApellido2())
			datosIdentificacionSE.addChildElement(new QName("apellido2")).addTextNode(dIdent.getApellido2());
	}

	/**
	 * Crea el apartado de datos de direccion en el SOAPElement
	 * 
	 * @param dDirec
	 * @param datosDireccionSE
	 * @throws SOAPException
	 */
	private void addDatosDireccion(DatosDireccionType dDirec, SOAPElement datosDireccionSE) throws SOAPException {
		if (null != dDirec.getPais())
			datosDireccionSE.addChildElement(new QName("pais")).addTextNode(String.valueOf(dDirec.getPais()));
		if (null != dDirec.getResidente())
			datosDireccionSE.addChildElement(new QName("residente")).addTextNode(String.valueOf(dDirec.getResidente()));
		if (null != dDirec.getProvincia())
			datosDireccionSE.addChildElement(new QName("provincia")).addTextNode(dDirec.getProvincia());
		if (null != dDirec.getMunicipio())
			datosDireccionSE.addChildElement(new QName("municipio")).addTextNode(dDirec.getMunicipio());

		datosDireccionSE.addChildElement(new QName("codLocalidad"))
				.addTextNode(String.valueOf(dDirec.getCodLocalidad()));
		if (null != dDirec.getLocalidad())
			datosDireccionSE.addChildElement(new QName("localidad")).addTextNode(dDirec.getLocalidad());
		if (null != dDirec.getTipoVia())
			datosDireccionSE.addChildElement(new QName("tipoVia")).addTextNode(dDirec.getTipoVia());
		if (null != dDirec.getCalle())
			datosDireccionSE.addChildElement(new QName("calle")).addTextNode(dDirec.getCalle());
		if (null != dDirec.getPortal())
			datosDireccionSE.addChildElement(new QName("portal")).addTextNode(dDirec.getPortal());
		if (null != dDirec.getLetra())
			datosDireccionSE.addChildElement(new QName("letra")).addTextNode(dDirec.getLetra());
		if (null != dDirec.getEscalera())
			datosDireccionSE.addChildElement(new QName("escalera")).addTextNode(dDirec.getEscalera());
		if (null != dDirec.getPiso())
			datosDireccionSE.addChildElement(new QName("piso")).addTextNode(dDirec.getPiso());
		if (null != dDirec.getPuerta())
			datosDireccionSE.addChildElement(new QName("puerta")).addTextNode(dDirec.getPuerta());
		if (null != dDirec.getCodigoPostal())
			datosDireccionSE.addChildElement(new QName("codigoPostal")).addTextNode(dDirec.getCodigoPostal());
	}

	/**
	 * Crea el apartado de datos bancarios en el SOAPElement
	 * 
	 * @param datosBanc
	 * @param datosBancariosSE
	 * @throws SOAPException
	 */
	private void addDatosBancarios(DatosBancariosType datosBanc, SOAPElement datosBancariosSE) throws SOAPException {
		datosBancariosSE.addChildElement(new QName("domiciliacion"))
				.addTextNode(String.valueOf(datosBanc.getDomiciliacion()));
		if (null != datosBanc.getIban())
			datosBancariosSE.addChildElement(new QName("iban")).addTextNode(datosBanc.getIban());
		if (null != datosBanc.getBic())
			datosBancariosSE.addChildElement(new QName("bic")).addTextNode(datosBanc.getBic());
		if (null != datosBanc.getMandato())
			datosBancariosSE.addChildElement(new QName("mandato")).addTextNode(datosBanc.getMandato());
		if (null != datosBanc.getFMandato())
			datosBancariosSE.addChildElement(new QName("fMandato")).addTextNode(datosBanc.getFMandato());
	}

	@Override
	public SOAPMessage callAnularLiquidacion(Comunicacion comunicacion) throws Exception {
		// TODO Auto-generated method stub
		return null;
	}
	
	/**
	 * Convierte la response de la invacion al WS en un objeto RespuestaType
	 * @param response
	 * @return
	 * @throws SOAPException
	 */
	private RespuestaType soapMessageToRespuestaType(SOAPMessage response) throws SOAPException {
		
		SOAPPart soapPart = response.getSOAPPart();
		SOAPEnvelope envelope = soapPart.getEnvelope();
		SOAPBody soapBody = envelope.getBody();
				
		NodeList nodesCodRespuesta = soapBody.getElementsByTagName("codRespuesta");
		NodeList nodesMensaje = soapBody.getElementsByTagName("mensaje");
		NodeList nodesFRespuesta = soapBody.getElementsByTagName("fRespuesta");
		NodeList nodesIdLiquidacionRespuesta = soapBody.getElementsByTagName("idLiquidacion");
		
		RespuestaType respuesta = new RespuestaType();
		respuesta.setCodRespuesta(null != nodesCodRespuesta.item(0)? nodesCodRespuesta.item(0).getTextContent() : null);
		respuesta.setMensaje(null != nodesMensaje.item(0)? nodesMensaje.item(0).getTextContent() : null);
		respuesta.setFRespuesta(null != nodesFRespuesta.item(0)? nodesFRespuesta.item(0).getTextContent() : null);
		respuesta.setIdLiquidacion(null != nodesIdLiquidacionRespuesta.item(0)? nodesIdLiquidacionRespuesta.item(0).getTextContent() : null);
		
		return respuesta;
	}
	
}
