package com.ejie.ab39a.utils;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.ejie.ab39a.constantes.Constantes;
import com.ejie.ab39a.model.CertificadoPDF;
import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Element;
import com.itextpdf.text.Image;
import com.itextpdf.text.Phrase;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.ColumnText;
import com.itextpdf.text.pdf.ICC_Profile;
import com.itextpdf.text.pdf.PdfAConformanceLevel;
import com.itextpdf.text.pdf.PdfAWriter;
import com.itextpdf.text.pdf.PdfContentByte;
import com.itextpdf.text.pdf.PdfGState;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.PdfStamper;
import com.itextpdf.text.pdf.PdfWriter;
import com.itextpdf.tool.xml.XMLWorker;
import com.itextpdf.tool.xml.XMLWorkerHelper;
import com.itextpdf.tool.xml.css.CssFile;
import com.itextpdf.tool.xml.css.StyleAttrCSSResolver;
import com.itextpdf.tool.xml.html.CssAppliers;
import com.itextpdf.tool.xml.html.CssAppliersImpl;
import com.itextpdf.tool.xml.html.Tags;
import com.itextpdf.tool.xml.parser.XMLParser;
import com.itextpdf.tool.xml.pipeline.css.CSSResolver;
import com.itextpdf.tool.xml.pipeline.css.CssResolverPipeline;
import com.itextpdf.tool.xml.pipeline.end.PdfWriterPipeline;
import com.itextpdf.tool.xml.pipeline.html.AbstractImageProvider;
import com.itextpdf.tool.xml.pipeline.html.HtmlPipeline;
import com.itextpdf.tool.xml.pipeline.html.HtmlPipelineContext;

/**
 * Clase que se encarga de la generaci�n, gesti�n y firma de los PDF
 * 
 *  
 * 
 */
public class UtilCertificados {

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

	private static final String TIPO_ICC = "sRGB IEC61966-2.1";
	private static final String URL_ICC = "http://www.color.org";
	private static final int INITIAL_LEADING = 12;

	private static final PdfAConformanceLevel NIVEL_PDFA = PdfAConformanceLevel.PDF_A_1B;

	/**
	 * Constructor por defecto
	 */
	private UtilCertificados() {
		super();
	}

	/**
	 * Metodo que crea un documento PDF/A
	 * 
	 * @param plantilla
	 *            InputStream con la plantilla HTML a aplicar
	 * @param pdfResultante
	 *            OutputStream con el documento
	 * @param certificadoPDF
	 *            datos del PDF
	 * @throws IOException
	 *             e e
	 * @throws DocumentException
	 *             e e
	 * 
	 */
	public static void generarPDF(InputStream plantilla, OutputStream pdfResultante,
			final CertificadoPDF certificadoPDF) throws IOException, DocumentException {

		UtilCertificados.logger.info("comenzamos a generar el PDF");
		Document document = null;
		PdfAWriter writer = null;
		Rectangle envelope = new Rectangle(Constantes.NUM_OCHOCIENTOS_CUARENTA_Y_DOS,
				Constantes.NUM_QUINIENTOS_NOVENTA_Y_CINCO);
		try {
			plantilla = UtilCertificados.reemplazarValoresVariables(plantilla, certificadoPDF);
			// step 1
			document = new Document();
			// step 2
			document.open();

			ByteArrayOutputStream pdfSinMarca = new ByteArrayOutputStream();

			writer = PdfAWriter.getInstance(document, pdfSinMarca, UtilCertificados.NIVEL_PDFA);

			writer.createXmpMetadata();

			writer.setInitialLeading(UtilCertificados.INITIAL_LEADING);
			writer.setTagged();
			// Orientacion
			document.setPageSize(envelope);
			// Prueba conteo
			document.setPageCount(1);
			// Compresion
			writer.setFullCompression();
			// step 3
			document.open();

			File file = new File(certificadoPDF.getRutaICC());
			ICC_Profile icc = ICC_Profile.getInstance(new FileInputStream(file));
			writer.setOutputIntents("Custom", "", UtilCertificados.URL_ICC, UtilCertificados.TIPO_ICC, icc);
			// writer.setOutputIntents(UtilCertificados.TIPO_ICC, "",
			// UtilCertificados.URL_ICC, UtilCertificados.TIPO_ICC, icc);

			// CSS stylesheet.
			CSSResolver cssResolver = new StyleAttrCSSResolver();
			for (String rutaCss : certificadoPDF.getRutasCss()) {
				CssFile cssFile = XMLWorkerHelper.getCSS(new FileInputStream(rutaCss));
				cssResolver.addCss(cssFile);
			}

			FontProvider fontProvider = new FontProvider();
			for (String rutaFuente : certificadoPDF.getRutasFuente()) {
				fontProvider.register(rutaFuente);
			}
			CssAppliers cssAppliers = new CssAppliersImpl(fontProvider);

			HtmlPipelineContext htmlContext = new HtmlPipelineContext(cssAppliers);
			htmlContext.setTagFactory(Tags.getHtmlTagProcessorFactory());
			htmlContext.setImageProvider(new AbstractImageProvider() {

				@Override
				public String getImageRootPath() {
					return certificadoPDF.getRutaImagenes();
				}
			});

			// Pipelines.
			PdfWriterPipeline pdf = new PdfWriterPipeline(document, writer);
			HtmlPipeline html = new HtmlPipeline(htmlContext, pdf);
			CssResolverPipeline css = new CssResolverPipeline(cssResolver, html);

			XMLWorker worker = new XMLWorker(css, true);
			XMLParser p = new XMLParser(worker);

			p.parse(plantilla);

			try {
				// se lanza una excepcion que no deberia, se captura. (parece
				// un bug de itext 5)
				document.close();
			} catch (Exception e) {
				UtilCertificados.logger.info("error pdf");
			}
			byte[] pdfSinMarcaBytes = pdfSinMarca.toByteArray();

			// Si hay que poner marca de agua se procesa
			// Si no hay que ponerla, escribimos el byteArray en el OutputStream
			// pdfResultante

			if (certificadoPDF.getPonerMarcaAgua()) {

				ByteArrayInputStream pdfSinMarcaAgua = new ByteArrayInputStream(pdfSinMarcaBytes);

				PdfReader pdfReader = new PdfReader(pdfSinMarcaAgua);

				int n = pdfReader.getNumberOfPages();

				PdfStamper stamper = new PdfStamper(pdfReader, pdfResultante);

				Image imageMarcaAgua = Image.getInstance(certificadoPDF.getRutaMarcaAgua());

				float anchura = imageMarcaAgua.getScaledWidth();
				float altura = imageMarcaAgua.getScaledHeight();
				// transparency
				PdfGState estadoPdf = new PdfGState();
				estadoPdf.setFillOpacity(Constantes.MARCA_AGUA_GRADO_TRANSPARENCIA);

				// properties
				PdfContentByte pagecontent;
				Rectangle pagesize;
				float posicionX, posicionY;
				// loop over every page
				for (int i = 1; i <= n; i++) {
					pagesize = pdfReader.getPageSizeWithRotation(i);
					posicionX = (pagesize.getLeft() + pagesize.getRight()) / Constantes.NUM_DOS;
					posicionY = (pagesize.getTop() + pagesize.getBottom()) / Constantes.NUM_DOS;
					pagecontent = stamper.getOverContent(i);
					pagecontent.saveState();
					pagecontent.setGState(estadoPdf);
					pagecontent.addImage(imageMarcaAgua, anchura, 0, 0, altura,
							posicionX - (anchura / Constantes.NUM_DOS), posicionY - (altura / Constantes.NUM_DOS));
					pagecontent.restoreState();
				}
				stamper.close();
				pdfReader.close();
			} else {

				if (certificadoPDF.isAddNumerosDePagina()) {
					ByteArrayInputStream pdfSinMarcaAgua = new ByteArrayInputStream(pdfSinMarcaBytes);

					PdfReader pdfReader = new PdfReader(pdfSinMarcaAgua);

					int n = pdfReader.getNumberOfPages();
					PdfStamper stamper = new PdfStamper(pdfReader, pdfResultante);
					PdfContentByte pagecontent;
					for (int i = 0; i < n;) {
						pagecontent = stamper.getOverContent(++i);
						ColumnText.showTextAligned(pagecontent, Element.ALIGN_RIGHT,
								new Phrase(String.format("%s / %s", i, n)), certificadoPDF.getPosXNumPag(),
								certificadoPDF.getPosYNumPag(), 0);
					}
					stamper.close();
					pdfReader.close();

				} else {

					pdfResultante.write(pdfSinMarcaBytes);
					pdfResultante.flush();
					pdfResultante.close();
				}
			}
		} finally {
			if (document != null && document.isOpen()) {
				document.close();
			}

			if (writer != null && !writer.isCloseStream()) {
				writer.close();
			}

			if (plantilla != null) {
				plantilla.close();
			}
		}
	}

	/**
	 * Generate PDF
	 * 
	 * @param htmlForPdf
	 *            String htmlForPdf
	 * @param fos
	 *            FileOutputStream fos
	 * @throws DocumentException
	 *             throws e
	 * @throws IOException
	 *             throws e
	 */
	public static void generatePreviewPDF(String htmlForPdf, FileOutputStream fos)
			throws DocumentException, IOException {
		// step 1
		Document document = new Document();
		// step 2

		PdfWriter writer = PdfWriter.getInstance(document, fos);
		// step 3
		document.open();
		// step 4
		XMLWorkerHelper.getInstance().parseXHtml(writer, document, IOUtils.toInputStream(htmlForPdf, "ISO-8859-1"));
		// step 5

		document.close();
		writer.close();

	}

	/**
	 * M�todo que se encarga de reemplazar los valores variables en las
	 * plantillas, si existiesen
	 * 
	 * @param plantilla
	 *            InputStream de la pantilla
	 * @param certificado
	 *            bean con los valores a reemplazar
	 * @return InputStream con los valores reemplazados
	 * @throws IOException
	 *             en caso de que haya algun problema con los datos
	 */
	private static InputStream reemplazarValoresVariables(InputStream plantilla, CertificadoPDF certificado)
			throws IOException {

		if (certificado.getDatosVariables() != null) {
			byte[] bytesPlantilla = new byte[plantilla.available()];

			plantilla.read(bytesPlantilla);

			String stringPlantilla = new String(bytesPlantilla);

			for (String clave : certificado.getDatosVariables().keySet()) {
				stringPlantilla = stringPlantilla.replace(clave, certificado.getDatosVariables().get(clave));
			}
			return new ByteArrayInputStream(stringPlantilla.getBytes());
		} else {
			return plantilla;
		}
	}
}
