/*
 * $Id: SOfficeDocConverter.java,v 1.14 2007/02/12 07:15:57 lawern Exp $
 *
 * Copyright (c) Siemens AG 2008 
 *           (c) Universität Paderborn 2008 
 * Licensed under EUPL 1.1. 
 *
 * $Log: SOfficeDocConverter.java,v $
 * Revision 1.14  2007/02/12 07:15:57  lawern
 * Überarbeitung und Anpassung an Linux
 *
 * Revision 1.13  2006/06/06 20:36:01  marbobo
 * + Mögliche NPE abgefangen
 *
 * Revision 1.12  2006/04/04 07:52:38  korowa
 * Funktion storeAsUrl() wurde wegen erzeugten Fehlern ersetzt durch storeToUrl()
 *
 * Revision 1.11  2006/03/10 09:05:06  korowa
 * +PowerPoint Presentationen können jetzt von OpenOffice konvertiert werden
 *
 * Revision 1.10  2004/11/10 15:02:44  koppy
 * Projektweite Überarbeitung von Error-Logs, Stacktraces,..
 *
 * Revision 1.9  2004/01/27 22:51:34  koppy
 * Verbindungstest zum OOServer (30s Timeout) nach Ausführung von quickstart
 *
 * Revision 1.8  2004/01/26 22:02:00  koppy
 * Pause in startOpenOffice() eingebaut
 *
 * Revision 1.7  2003/12/24 10:12:01  bigmac
 * Projektweite Import-Optimierung
 *
 * Revision 1.6  2003/07/28 11:01:13  lawern
 * SoWordDocConverter hinzugefügt
 *
 * Revision 1.5  2003/07/25 12:07:04  lawern
 * Umstellung auf URLs und mehrere Konvertierungen
 *
 * Revision 1.4  2003/07/18 14:13:24  teng
 * Statische Variable sofficeProcess
 *
 * Revision 1.3  2003/07/18 13:38:48  teng
 * Bei der Instanzierung wird zuerst ueberprueft, ob OpenOffice von VKC gestartet wurde. Wenn nicht, starten OpenOffice an dieser Stelle.
 *
 * Revision 1.2  2003/07/10 13:13:06  lawern
 * Fenster werden jetzt im Hintergrund geöffnet.
 *
 */
package vkc.converter;

import java.io.File;
import java.net.InetSocketAddress;
import java.net.MalformedURLException;
import java.net.Socket;
import java.net.URL;
import java.util.StringTokenizer;

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

import com.sun.star.beans.PropertyValue;
import com.sun.star.bridge.XUnoUrlResolver;
import com.sun.star.frame.XComponentLoader;
import com.sun.star.frame.XStorable;
import com.sun.star.lang.XComponent;
import com.sun.star.lang.XMultiComponentFactory;
import com.sun.star.uno.UnoRuntime;
import com.sun.star.uno.XComponentContext;

public class SOfficeDocConverter {
	private static String port = "";

	private static String host = "";

	private static boolean officeStarted = false;

	private static File openOffice = null;

	private static XComponentLoader xcomponentloader = null;

	/**
	 * Initialisiert die Verbindung zum OpenOffice Server
	 */
	public static void init() {
		String remoteObjectDescription = "uno:socket,host=" + host + ",port=" + port + ";urp;StarOffice.ServiceManager";

		try {
			/* Bootstraps a component context with the jurt base components
			 registered. Component context to be granted to a component for running.
			 Arbitrary values can be retrieved from the context. */
			XComponentContext xComponentContext = com.sun.star.comp.helper.Bootstrap.createInitialComponentContext(null);

			/* Gets the service manager instance to be used (or null). This method has
			 been added for convenience, because the service manager is a often used
			 object. */
			XMultiComponentFactory xMultiComponentFactory = xComponentContext.getServiceManager();

			/* Creates an instance of the component UnoUrlResolver which
			 supports the services specified by the factory. */
			Object objectUrlResolver = xMultiComponentFactory.createInstanceWithContext("com.sun.star.bridge.UnoUrlResolver", xComponentContext);

			// Create a new url resolver
			XUnoUrlResolver xurlresolver = (XUnoUrlResolver) UnoRuntime.queryInterface(XUnoUrlResolver.class, objectUrlResolver);

			// Resolves an object that is specified as follow:
			// uno:<connection description>;<protocol description>;<initial object name>
			Object objectInitial = xurlresolver.resolve(remoteObjectDescription);

			// Create a service manager from the initial object
			xMultiComponentFactory = (XMultiComponentFactory) UnoRuntime.queryInterface(XMultiComponentFactory.class, objectInitial);

			// Query for the XPropertySet interface.
			//            XPropertySet xpropertysetMultiComponentFactory = (XPropertySet) UnoRuntime.queryInterface(XPropertySet.class,
			//                    xMultiComponentFactory);
			// Get the default context from the office server.
			//            Object objectDefaultContext = xpropertysetMultiComponentFactory.getPropertyValue(
			//                    "DefaultContext");
			// Query for the interface XComponentContext.
			//            xComponentContext = (XComponentContext) UnoRuntime.queryInterface(XComponentContext.class,
			//                    objectDefaultContext);
			/* A desktop environment contains tasks with one or more
			 frames in which components can be loaded. Desktop is the
			 environment for components which can instanciate within
			 frames. */
			xcomponentloader = (XComponentLoader) UnoRuntime.queryInterface(
					XComponentLoader.class, xMultiComponentFactory
							.createInstanceWithContext(
									"com.sun.star.frame.Desktop",
									xComponentContext));
		} catch (Exception exp) {
			VKCLogManager.errorLog("SOfficeDocConverter", "1", "Can't connect to OpenOffice!", exp);
		}
	}

	/**
	 * Convert a given file to another types and save it as a new file.
	 * 
	 * <p>
	 * You find the file type definitions in
	 * $(office_install_dir)/share/config/registry/instance/org/openoffice/Office/TypeDetection.xml
	 * </p>
	 * 
	 * <p>
	 * Type definition for HTML = "swriter: HTML (StarWriter)"
	 * </p>
	 * 
	 * <p>
	 * For RTF= "Rich Text Format"
	 * </p>
	 *
	 * @param file given to convert
	 * @param convertTypes the new file types.
	 * @param files the new files
	 */
	public void convert(URL url, String[] convertTypes, URL[] urls) {
		if (xcomponentloader == null) {
			//OpenOffice initialisieren, wenn noch nicht passiert
			init();
		}

		try {
			String stringUrl;

			//Url korrigieren, wenn sie noch keine SOffice file-Url hat.
			if (url.getProtocol().equals("file")) {
				stringUrl = "file://" + url.getPath();
			} else {
				stringUrl = url.toString();
			}

			// create Hidden Property
			PropertyValue[] propertyvalueHidden = new PropertyValue[1];
			propertyvalueHidden[0] = new PropertyValue();
			propertyvalueHidden[0].Name = "Hidden";
			propertyvalueHidden[0].Value = new Boolean(true);

			// Loading the wanted document
			XComponent objectDocumentToStore = xcomponentloader.loadComponentFromURL(stringUrl, "_blank", 0, propertyvalueHidden);

			// Getting an object that will offer a simple way to store a document to a URL.
			XStorable xstorable = (XStorable) UnoRuntime.queryInterface(XStorable.class, objectDocumentToStore);

			// Preparing properties for converting the document
			PropertyValue[] propertyvalue = new PropertyValue[2];

			for (int i = 0; i < convertTypes.length; i++) {
				// Setting the flag for overwriting
				propertyvalue[0] = new PropertyValue();
				propertyvalue[0].Name = "Overwrite";
				propertyvalue[0].Value = new Boolean(true);

				// Setting the filter name
				propertyvalue[1] = new PropertyValue();
				propertyvalue[1].Name = "FilterName";
				propertyvalue[1].Value = convertTypes[i];

				// Url korrigieren, wenn sie noch keine SOffice file-Url hat.
				if (urls[i].getProtocol().equals("file")) {
					stringUrl = "file://" + urls[i].getPath();
				} else {
					stringUrl = urls[i].toString();
				}
				// Storing and converting the document 
				xstorable.storeToURL(stringUrl, propertyvalue);
			}

			// Closing the converted document
			objectDocumentToStore.dispose();
		} catch (Exception ex) {
			VKCLogManager.errorLog(this, "2", "Can't convert Document!", ex);
		}
	}

	/**
	 * Started den OpenOffice Server
	 *
	 * @param _openOffice Pfad zum Executable
	 */
	public static void startOpenOffice(String _openOffice, String _host, String _port) {
		host = _host;
		port = _port;

		//OpenOffice noch starten, wenn Initialisierung fehlgeschlagen
		if (xcomponentloader == null) {

            StringTokenizer st = new StringTokenizer(_openOffice, " ");
			String[] cmdarr = new String[st.countTokens()];
            
            int i=0;
            String curToken;
            while (st.hasMoreElements()) {
                curToken = st.nextToken();
                if (i == 0) {
                    openOffice = new File(curToken);
                }
                cmdarr[i++] = curToken;
            }

			VKCProcess process = new VKCProcess(cmdarr, null, openOffice.getParentFile());
			process.execute();

			long timeout = 30000;
			long start = System.currentTimeMillis();
			VKCLogManager.infoLog(SOfficeDocConverter.class, "Trying to connect to OOoServer. Timeout = " + (timeout / 1000));

			while (start + timeout > System.currentTimeMillis()) {
				try {
					// Tests connection to the OOoServer
					// (Waits until the OOserver is up)
					Socket s = new Socket();
					s.bind(new InetSocketAddress(0));
					s.connect(new InetSocketAddress(host, Integer.parseInt(port)), 5000);
					s.close();
					officeStarted = true;
					break;
				} catch (Exception e) {
				}
			}

			if (!officeStarted) {
				VKCLogManager.errorLog(SOfficeDocConverter.class, "", "Connection to OOoServer failed!");

				return;
			}
			//OpenOffice initialisieren
			init();
		}
	}

	/**
	 * Beendet den OpenOffice Server, falls er über startOpenOffice gestarted
	 * wurde.
	 */
	public static void stopOpenOffice() {
		if (officeStarted) {
			String[] cmdarr = new String[2];
			cmdarr[0] = openOffice.toString();
			cmdarr[1] = "-killtray";

			VKCProcess process = new VKCProcess(cmdarr, null, openOffice.getParentFile());
			process.execute();

			officeStarted = false;
		}
	}

	public static void main(String[] args) {
		VKCLogManager.init("c:/temp/test_", ".log");
		VKCLogManager.setEnableLogging(true);
		VKCLogManager.setLogToConsole(true);

		if (args.length < 3) {
			System.out.println("Usage: java DocumentConverter fileToConvert newFileType fileExtension [host port]");
			System.out.println("If host and port are not set, localhost 8100 will be used. ");
			System.out.println("Example for Word to html:java DocumentConverter test.doc \"swriter: HTML (StarWriter)\" \"html\" localhost 8100");
			System.out.println("Example for Word to RTF:java DocumentConverter test.doc \"Rich Text Format\" \"rtf\" localhost 8100");
			System.exit(1);
		}

		// parse origin file name without extension
		String destFile = args[0].substring(0, args[0].indexOf('.'));

		// Appending the extension to the origin document name
		destFile = destFile + "." + args[2];

		if (args.length > 4) {
			host = args[3];
			port = args[4];
		} else {
			port = "8100";
			host = "localhost";
		}

		String[] convertTypes = new String[1];
		URL[] urls = new URL[1];
		convertTypes[0] = args[1];

		try {
			urls[0] = new File(destFile).toURL();

			new SOfficeDocConverter().convert(new File(args[0]).toURL(), convertTypes, urls);
		} catch (MalformedURLException e1) {
			e1.printStackTrace();
		}

		System.exit(0);
	}
}