/*
 * $Id: PdfDocConverter.java,v 1.22 2007/07/10 11:24:02 marbobo Exp $
 *
 * Copyright (c) Siemens AG 2008 
 *           (c) Universität Paderborn 2008 
 * Licensed under EUPL 1.1. 
 *
 * $Log: PdfDocConverter.java,v $
 * Revision 1.22  2007/07/10 11:24:02  marbobo
 * + Bugfix: Dateiname die an ProcessKiller übergeben wird korrigiert
 *
 * Revision 1.21  2007/02/12 07:15:57  lawern
 * Überarbeitung und Anpassung an Linux
 *
 * Revision 1.20  2006/06/06 20:36:03  marbobo
 * + Mögliche NPE abgefangen
 *
 * Revision 1.19  2006/04/07 10:10:33  korowa
 * Nur erlaubte Konvertierungen werden durchgeführt(DocConverter:conversions) bzw. zurückgeliefert.
 *
 * Revision 1.18  2005/03/30 13:24:19  lawern
 * UTF-8-Kodierung eingestellt.
 *
 * Revision 1.17  2005/02/01 10:32:33  lawern
 * Ausgabe von Warnings bei pdftohtml unterdrückt: Führt zum Aufhängen.
 *
 * Revision 1.16  2004/11/10 15:02:43  koppy
 * Projektweite Überarbeitung von Error-Logs, Stacktraces,..
 *
 * Revision 1.15  2004/11/02 10:13:55  lawern
 * Bugfixes
 *
 * Revision 1.14  2004/07/14 06:36:52  lawern
 * Fehler abgefangen
 *
 * Revision 1.13  2004/06/27 17:12:47  marbobo
 * + Quellcode maintenance
 *
 * Revision 1.12  2003/11/29 22:13:52  koppy
 * InputStreams in VKCInputStream gekappselt
 *
 * Revision 1.11  2003/10/01 08:49:25  lawern
 * Html-Versionen auf Frames umgestellt
 *
 * Revision 1.10  2003/09/24 15:00:15  lawern
 * von getPath auf getCannonicalPath umgestellt
 *
 * Revision 1.9  2003/09/24 14:34:23  lawern
 * Metadaten werden jetzt aus PDF-Dokument übernommen.
 *
 * Revision 1.8  2003/09/12 12:14:26  lawern
 * Links werden jetzt extrahiert.
 *
 * Revision 1.7  2003/09/11 08:27:23  lawern
 * einige Methoden public gemacht
 *
 * Revision 1.6  2003/07/24 11:51:22  lawern
 * Umstellung auf XmlExtractor
 *
 * Revision 1.5  2003/06/26 15:15:22  lawern
 * XLS und PPT eingefügt und Konverter überarbeitet
 *
 * Revision 1.4  2003/06/18 12:42:44  lawern
 * *** empty log message ***
 *
 * Revision 1.3  2003/06/18 11:36:03  lawern
 * erste funktionsfähige Version
 *
 *
 */
package vkc.converter;

import vkc.misc.ProcessKiller;
import vkc.misc.VKCLogManager;
import vkc.misc.VKCProcess;

import vkc.util.FileUtils;
import vkc.util.VKCInputStream;
import vkc.util.ZipUtils;

import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

import java.net.MalformedURLException;
import java.net.URL;

import java.util.ArrayList;
import java.util.Collection;

/**
 * Klasse zum Konvertieren von Textdokumenten.
 *
 * @author lawern
 */
public class PdfDocConverter extends DocConverter {
	private static final boolean debug = false;

	public static String docSuffix = ".pdf";

	private XmlExtractor xmlExtractor;

	private String[] cmdarr;

	private String[] env = new String[2];

	private File dir = null;

	private File zipFile = null;

	private String htmlFile = null;

	private File htmlDir = null;

	//  Default Timeout auf 10 min setzen
	protected int timeout = 10;

	public PdfDocConverter() {
	}

	/**
	 * Liefert den Suffix für temporäre Dateien zurück.
	 *
	 * @return docSuffix
	 */
	public String getDocSuffix() {
		return docSuffix;
	}

	/**
	 * Diese Methode konvertiert das übergebene Dokument in die in der
	 * Collection definierten Formate. Der Parameter conversions wird zur Zeit
	 * noch nicht ausgewertet.
	 *
	 * @param docFile URL des zu konvertierendes Dokument
	 * @param conversions Collection von VKCDocumentTypes welche die zu
	 *        erzeugenden Konvertierungen beschreibt.
	 */
	public void convert(URL docFile, Collection conversions) {
		if (debug) {
			System.out.println("PdfDocConverter: " + docFile);
		}

		//String docFileString = VKCUtils.getLocalPath(docFile);
		VKCProcess process = null;

		try {
			dir = new File(basePath).getCanonicalFile();
		} catch (IOException ioe) {
			VKCLogManager.errorLog(this, "7", "Base path not found!", ioe);
		}

		InputStream docStream = null;
		File tmpFile = null;

		try {
			//Datei von Url in temporäre Datei schreiben
			docStream = docFile.openStream();

			if (docStream != null) {
				tmpFile = File.createTempFile("PdfDoc", ".pdf");

				if (!debug) {
					tmpFile.deleteOnExit();
				}

				FileUtils.saveInputStream(tmpFile, docStream, true);
				FileUtils.closeInputStream(docStream);

				try {
					htmlDir = File.createTempFile("PdfDoc", "");
					htmlDir.delete();
					htmlDir.mkdir();

					if (!debug) {
						htmlDir.deleteOnExit();
					}

					htmlFile = htmlDir.getCanonicalPath() + File.separator + "index.html";
				} catch (java.io.IOException ioe) {
					VKCLogManager.errorLog(this, "3",
							"Can't create temp-file.", ioe);
				}

				// Environment-Variablen setzen
				env[0] = "path=" + dir + File.separator + "gs" + File.separator
						+ "bin;" + dir + File.separator + "gs" + File.separator
						+ "lib";
				env[1] = "GS_LIB=" + dir + File.separator + "fonts;" + dir
						+ File.separator + "gs" + File.separator + "lib";

				// Kommando-Array mit Voreinstellung für den Konverter
				// initialisieren
                if (windows) {
                    cmdarr = new String[9];
                    cmdarr[0] = "cmd";
                    cmdarr[1] = "/c";
                    cmdarr[2] = "pdftohtml";
                    cmdarr[3] = "-enc";
                    cmdarr[4] = "UTF-8";
                    cmdarr[5] = "-c";
                    cmdarr[6] = "-q";
                    cmdarr[7] = tmpFile.getCanonicalPath();
                    cmdarr[8] = htmlFile;

                    String pdfFile = cmdarr[7].substring(cmdarr[7]
                            .lastIndexOf(File.separator) + 1);
                    ProcessKiller killer = new ProcessKiller(pdfFile,
                            timeout + 1);
                    killer.start();
                    process = new VKCProcess(cmdarr, env, dir);
                    process.executeAndWaitWithTimeout(timeout * 60);
                    killer.interrupt();
                } else {
                    cmdarr = new String[7];
                    cmdarr[0] = "./pdftohtml";
                    cmdarr[1] = "-enc";
                    cmdarr[2] = "UTF-8";
                    cmdarr[3] = "-c";
                    cmdarr[4] = "-q";
                    cmdarr[5] = tmpFile.getCanonicalPath();
                    cmdarr[6] = htmlFile;

                    process = new VKCProcess(cmdarr, env, dir);
                    process.executeAndWaitWithTimeout(timeout * 60);
                }

				if (htmlFile != null) {
					FileUtils.deleteZeroByteFile(new File(htmlFile));
				}

				if (htmlFile != null && !new File(htmlFile).exists()) {
					VKCLogManager.errorLog(this, "6", "HTML conversion not generated.");

					try {
						FileUtils.deleteDirectory(htmlDir);
					} catch (Exception ex) {
						VKCLogManager.errorLog(this, "", ex.getMessage(), ex);
					}
				} else {
					try {
						//Html-Version zusammenzipen
						zipFile = File.createTempFile("PdfDoc", ".zip");

						if (!debug) {
							zipFile.deleteOnExit();
						}

						ZipUtils.zip(htmlDir, zipFile, true, htmlDir.getCanonicalPath() + File.separator);
					} catch (java.io.IOException ioe) {
						VKCLogManager.errorLog(this, "4", "Can't create temp-file.", ioe);
					}

					//Html-Verzeichnis löschen
					try {
						FileUtils.deleteDirectory(htmlDir);
					} catch (Exception ex) {
						VKCLogManager.errorLog(this, "5", "Can't delete temp-dir.", ex);
					}
				}
				
				if (enableTdf) {
					//XML-Datei erzeugen
					File xmlFile = File.createTempFile("PdfDoc", "");
                    if (windows) {
                        cmdarr = new String[9];
                        cmdarr[0] = "cmd";
                        cmdarr[1] = "/c";
                        cmdarr[2] = "pdftohtml";
                        cmdarr[3] = "-enc";
                        cmdarr[4] = "UTF-8";
                        cmdarr[5] = "-xml";
                        cmdarr[6] = "-q";
                        cmdarr[7] = tmpFile.getCanonicalPath();
                        cmdarr[8] = xmlFile.getCanonicalPath();
                        process = new VKCProcess(cmdarr, env, dir);
                        process.executeAndWaitWithTimeout(timeout * 60);
                    } else {
                        cmdarr = new String[7];
                        cmdarr[0] = "./pdftohtml";
                        cmdarr[1] = "-enc";
                        cmdarr[2] = "UTF-8";
                        cmdarr[3] = "-xml";
                        cmdarr[4] = "-q";
                        cmdarr[5] = tmpFile.getCanonicalPath();
                        cmdarr[6] = xmlFile.getCanonicalPath();
                        process = new VKCProcess(cmdarr, env, dir);
                        process.executeAndWaitWithTimeout(timeout * 60);
                    }

					//XML-File korrigieren, da pdftohtml das .xml selbst anhängt
					String xmlFileName = xmlFile.getCanonicalPath();
					xmlFile.delete();
					xmlFile = new File(xmlFileName + ".xml");

					if (!debug) {
						xmlFile.deleteOnExit();
					}

					FileUtils.deleteZeroByteFile(xmlFile);

					if (xmlFile.exists()) {
						//TDL-Stream erzeugen
						xmlExtractor = new XmlExtractor();

						try {
							xmlExtractor.analyse(xmlFile.toURL().toString());
						} catch (MalformedURLException e) {
						}

						xmlFile.delete();
					}
				}
			} else {
				VKCLogManager.errorLog(this, "9",
						"Can't open stream from url: " + docFile);
			}
		} catch (Exception e) {
			if (tmpFile != null) {
				VKCLogManager.errorLog(this, "8", "Can't create temp-file: "
						+ tmpFile.getPath(), e);
			} else {
				VKCLogManager.errorLog(this, "8", "Can't create temp-file!", e);
			}

			FileUtils.closeInputStream(docStream);
		} finally {
			//temporäre Dokumentendatei löschen
			if (!debug) {
				if (tmpFile != null) {
					tmpFile.delete();
				}
			}
		}
	}

	/**
	 * Liefert nach dem Aufruf der convert-Methode den Stream der
	 * Tdf-Konvertierung.
	 *
	 * @return Stream der Tdf-Konvertierung oder <code>null</code> falls er
	 *         nicht existiert
	 */
	public VKCInputStream getTdfStream() {
		if (xmlExtractor != null && enableTdf) {
			byte[] tdfArray = xmlExtractor.getTdfArray();

			return new VKCInputStream(new ByteArrayInputStream(tdfArray), tdfArray.length);
		}

		return null;
	}

	/**
	 * Liefert nach dem Aufruf der convert-Methode den Stream der
	 * HTML-Konvertierung.
	 *
	 * @return Stream der HTML-Konvertierung oder <code>null</code> falls er
	 *         nicht existiert
	 */
	public VKCInputStream getHtmlStream() {
		if (zipFile != null && enableHtml) {
			incOpenStreams();

			try {
				return new VKCInputStream(new BufferedInputStream(new FileInputStream(zipFile)), zipFile.length()) {
					public void close() throws IOException {
						super.close();
						closeHtmlStream();
					}
				};
			} catch (Exception e) {
				VKCLogManager.errorLog(this, "2", "ZIP-File not found!", e);
			}
		}

		return null;
	}

	/**
	 * Liefert den Titel des Dokuments zurück.
	 *
	 * @return Titel des Dokuments oder <code>null</code>
	 */
	public String getTitle() {
		if (xmlExtractor != null) {
			return xmlExtractor.getDocTitle();
		}

		return null;
	}

	/**
	 * Liefert die Beschreibung des Dokuments zurück.
	 *
	 * @return Beschreibung des Dokuments oder <code>null</code>
	 */
	public String getDescription() {
		if (xmlExtractor != null) {
			return xmlExtractor.getDocSubject();
		}

		return null;
	}

	/**
	 * Liefert die Schlagwörter des Dokuments zurück.
	 *
	 * @return Schlagwörter des Dokuments oder <code>null</code>
	 */
	public String getKeyWords() {
		if (xmlExtractor != null) {
			return xmlExtractor.getDocKeywords();
		}

		return null;
	}

	/**
	 * Liefert eine Collection mit Objekten vom Typ TdfElement zurück, welche
	 * das Dokument beschreiben.
	 *
	 * @return Collection mit Objekten vom Typ TdfElement
	 */
	public Collection getElements() {
		if (xmlExtractor != null) {
			return xmlExtractor.getElements();
		}

		return null;
	}

	/**
	 * Gibt eine Liste zurueck, der alle HREFs einer Seite enthaelt.
	 *
	 * @return Link-Vektor
	 */
	public ArrayList getLinks() {
		return xmlExtractor.getLinks();
	}

	/**
	 * Löschen der TDF-Streams
	 */
	void closeTdfStream() {
		if (debug) {
			System.out.println("closeTdfStream");
		}

		decOpenStreams();
	}

	/**
	 * Löschen des Ascii-Streams
	 */
	void closeHtmlStream() {
		if (debug) {
			System.out.println("closeHtmlStream");
		}

		decOpenStreams();

		//ZIP-Datei löschen
		if (zipFile != null) {
			zipFile.delete();
		}
	}

	/**
	 * Schließt das Konverter-Objekt und fügt es wieder in den Konverter-Pool
	 * ein.
	 */
	final void closeConverter() {
		//temporäre Dateien löschen
		if (zipFile != null) {
			zipFile.delete();
		}
		//this-Referenz wieder in die Queue einfügen
	}
}