/*******************************************************************************
 * This file is part of GECAMed.
 * 
 * GECAMed is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License (L-GPL) as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * GECAMed is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License (L-GPL)
 * along with GECAMed.  If not, see <http://www.gnu.org/licenses/>.
 * 
 * GECAMed is Copyrighted by the Centre de Recherche Public Henri Tudor (http://www.tudor.lu)
 * (c) CRP Henri Tudor, Luxembourg, 2008
 *******************************************************************************/
package lu.tudor.santec.gecamed.core.gui.controller.document.writer;

import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.StringSelection;
import java.io.File;
import java.io.InputStream;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;

import javax.print.PrintService;
import javax.print.PrintServiceLookup;

import lu.tudor.santec.gecamed.core.gui.controller.data.HtmlTransferable;
import lu.tudor.santec.gecamed.core.gui.controller.document.DocumentController;
import lu.tudor.santec.gecamed.core.test.TestUtils;
import lu.tudor.santec.gecamed.core.utils.GECAMedUtils;
import lu.tudor.santec.gecamed.core.utils.controller.utils.FileUtils;
import ooo.connector.BootstrapSocketConnector;

import org.apache.log4j.Level;
import org.apache.log4j.Logger;

import com.sun.star.awt.XTopWindow;
import com.sun.star.beans.PropertyValue;
import com.sun.star.document.XEventBroadcaster;
import com.sun.star.frame.XComponentLoader;
import com.sun.star.frame.XController;
import com.sun.star.frame.XDispatchHelper;
import com.sun.star.frame.XDispatchProvider;
import com.sun.star.frame.XFrame;
import com.sun.star.frame.XModel;
import com.sun.star.lang.XComponent;
import com.sun.star.lang.XMultiComponentFactory;
import com.sun.star.text.XText;
import com.sun.star.text.XTextCursor;
import com.sun.star.text.XTextDocument;
import com.sun.star.text.XTextRange;
import com.sun.star.text.XTextViewCursor;
import com.sun.star.text.XTextViewCursorSupplier;
import com.sun.star.text.XWordCursor;
import com.sun.star.uno.RuntimeException;
import com.sun.star.uno.UnoRuntime;
import com.sun.star.uno.XComponentContext;
import com.sun.star.uno.XInterface;
import com.sun.star.util.CloseVetoException;
import com.sun.star.util.XCloseBroadcaster;
import com.sun.star.util.XCloseable;
import com.sun.star.util.XSearchDescriptor;
import com.sun.star.util.XSearchable;
import com.sun.star.view.XPrintable;

//Controller for OpenOffice Writer
public class WriterController extends DocumentController 
{
	/* ======================================== */
	// 		CONSTANTS
	/* ======================================== */
	
	private static final String DOCUMENT_EXTENSION = ".odt";

	private static final String DOCUMENT_TEMPLATE_PATH = "resources/template.odt";
	
	private static final String HTML	= "<html>";
	
	private static final String SEARCH_NEW_STYLE	= "\\[[A-Z0-9_]+\\]";
	
	private static final String	SEARCH_OLD_STYLE	= "\\<[A-Z_][A-Z0-9_]+\\>";
	
	private static final Clipboard CLIPBOARD = Toolkit.getDefaultToolkit().getSystemClipboard();

	private static final PropertyValue[] printerDescription 		= new PropertyValue[1];
	
	private static final PropertyValue[] printerOptions 			= new PropertyValue[1];
	
	private static final PropertyValue[] openOptions 				= new PropertyValue[1];
	
	private static final PropertyValue[] pasteSpecialOptions		= new PropertyValue[1];
	
	private static final PropertyValue[] minimumFontSizeOptions		= new PropertyValue[3];
	
	private static final PropertyValue[] minimumMarginSizeOptions	= new PropertyValue[4];
	
	private static final PropertyValue[] move1Options				= new PropertyValue[1];
	
	
	static
	{
		printerDescription[0] 		= new PropertyValue();
		printerDescription[0].Name 	= "Name";
		printerDescription[0].Value = null;
		
		printerOptions[0] 			= new PropertyValue();
		printerOptions[0].Name 		= "Pages";
		printerOptions[0].Value 	= "1-";
		
		openOptions[0] 				= new PropertyValue();
		openOptions[0].Name 		= "Hidden";
		openOptions[0].Value 		= null;
		
		pasteSpecialOptions[0]		= new PropertyValue();
		pasteSpecialOptions[0].Name	= "SelectedFormat";
		pasteSpecialOptions[0].Value= Integer.valueOf(51);
		
		move1Options[0]			= new PropertyValue();
		move1Options[0].Name	= "Count";
		move1Options[0].Value	= Integer.valueOf(1);
		
		minimumFontSizeOptions[0]			= new PropertyValue();
		minimumFontSizeOptions[0].Name		= "FontHeight.Height";
		minimumFontSizeOptions[0].Value		= Integer.valueOf(2);
		minimumFontSizeOptions[1]			= new PropertyValue();
		minimumFontSizeOptions[1].Name		= "FontHeight.Prop";
		minimumFontSizeOptions[1].Value		= Integer.valueOf(100);
		minimumFontSizeOptions[2]			= new PropertyValue();
		minimumFontSizeOptions[2].Name		= "FontHeight.Diff";
		minimumFontSizeOptions[2].Value		= Integer.valueOf(0);
		
		minimumMarginSizeOptions[0]			= new PropertyValue();
		minimumMarginSizeOptions[0].Name	= "TopBottomMargin.TopMargin";
		minimumMarginSizeOptions[0].Value	= Integer.valueOf(0);
		minimumMarginSizeOptions[1]			= new PropertyValue();
		minimumMarginSizeOptions[1].Name	= "TopBottomMargin.BottomMargin";
		minimumMarginSizeOptions[1].Value	= Integer.valueOf(0);
		minimumMarginSizeOptions[2]			= new PropertyValue();
		minimumMarginSizeOptions[2].Name	= "TopBottomMargin.TopRelMargin";
		minimumMarginSizeOptions[2].Value	= Integer.valueOf(100);
		minimumMarginSizeOptions[3]			= new PropertyValue();
		minimumMarginSizeOptions[3].Name	= "TopBottomMargin.BottomRelMargin";
		minimumMarginSizeOptions[3].Value	= Integer.valueOf(100);
		
	}
	
	
	/* ======================================== */
	// 		MEMBERS
	/* ======================================== */
	
	private static Logger logger = Logger.getLogger(WriterController.class.getName());
	
	private XInterface				oDesktop;
	
	private XComponentContext		xContext;
	
	private XMultiComponentFactory	xMCF;
	
	private XComponent				xComp;
	
	private String					ooInstFolder;
	
	private CloseListener			closeListener;
	
	private XController				xController;
	
	private XDispatchHelper			dispatchHelper;
	
	private XDispatchProvider		dispatchProvider;
	
	private XTextViewCursor			textViewCursor;
	
	private HtmlTransferable 		transferable;

	private XSearchable				searchable;
	
	private XSearchDescriptor		searchDesc;
	
	
	
	/* ======================================== */
	// 		CONSTRUCTORS
	/* ======================================== */
	
	public WriterController(String ooInstFolder) 
	{
		// Initiating a new state class
		this.ooInstFolder	= ooInstFolder;
//		this.transferable	= new HtmlTransferable();
	}
		
	/* ======================================== */
	// 		CLASS BODY
	/* ======================================== */
	
	protected void open() throws Exception 
	{
		// Setting the local installation folder of OpenOffice
		// Starting OO and loading Document
		xContext 	= (XComponentContext) WriterController.connect(ooInstFolder);
		xMCF 		= xContext.getServiceManager();
		oDesktop 	= (XInterface) xMCF.createInstanceWithContext("com.sun.star.frame.Desktop", xContext);
		XComponentLoader xCompLoader = (XComponentLoader) UnoRuntime.queryInterface(
				XComponentLoader.class, oDesktop);
		
		String file_path= "file:///" + FileUtils.adeptFilePath(file.getAbsolutePath());
		
		openOptions[0].Value = openedHidden ? Boolean.TRUE : Boolean.FALSE;
		xComp = xCompLoader.loadComponentFromURL(file_path, "_default", 0, openOptions);
		
		XModel sModel 	= (XModel)UnoRuntime.queryInterface(XModel.class, xComp);
		xController 	= sModel.getCurrentController();
		XFrame sFrame 	= xController.getFrame();
		dispatchProvider= (XDispatchProvider)UnoRuntime.queryInterface(
				XDispatchProvider.class, sFrame);
		dispatchHelper 	= (XDispatchHelper)UnoRuntime.queryInterface(XDispatchHelper.class, 
				xMCF.createInstanceWithContext("com.sun.star.frame.DispatchHelper", xContext));
		
		this.addSaveListener(new SaveListener(getState(), this));
		closeListener 	= new CloseListener();
		closeListener.setBQuit(false);
		this.addCloseListener(closeListener);
		bringDocumentInFront();
	}
	
	
	public void addSaveListener(SaveListener listener) 
	{
		// Save Listener
		final XEventBroadcaster xEVB = (XEventBroadcaster) UnoRuntime
				.queryInterface(XEventBroadcaster.class, xComp);
		listener.setXEVB(xEVB);
		xEVB.addEventListener(listener);
	}
	
	public void addCloseListener(CloseListener listener)
	{
		// Close Listener
		final XCloseBroadcaster xCB = (XCloseBroadcaster) UnoRuntime
				.queryInterface(XCloseBroadcaster.class, xComp);
		listener.setXCB(xCB);
		xCB.addCloseListener(listener);
	}
	
	
	public void append(String text) 
	{
		dispatchHelper.executeDispatch(dispatchProvider, ".uno:GoToEndOfDoc", "", 0, null);
		XTextDocument oText = (XTextDocument) UnoRuntime.queryInterface(
				XTextDocument.class, xComp);
		
		XText xText = oText.getText();
		xText.insertString(xText.getEnd(),text, true);				
	}
	
	
	public void pasteHtml (String html)
	{
		try
		{
			clearClipBoard();
			if (isOpenOffice())
			{
				// copy HTML via temporary OO document
				copyToClipboard(html, ".html");
				// paste the copied text from the temp doc
				dispatchHelper.executeDispatch(dispatchProvider, ".uno:ClipboardFormatItems", "", 0, pasteSpecialOptions);
				dispatchHelper.executeDispatch(dispatchProvider, ".uno:SwBackspace", "", 0, null);
			}
			else
			{
				// copy the HTML text into the clipboard
				CLIPBOARD.setContents(getHtmlTransferable(html), null);
				// paste the HTML text into the document
				dispatchHelper.executeDispatch(dispatchProvider, ".uno:Paste", "", 0, null);
				// do a backspace to remove the '<' sign pasted with the HTML
				dispatchHelper.executeDispatch(dispatchProvider, ".uno:SwBackspace", "", 0, null);
			}
		}
		catch (Exception e)
		{
			logger.log(Level.ERROR, e.getMessage(), e);
		} finally {
			clearClipBoard();
		}
	}

	public void print()
	{
		try 
		{
			PrintService printer = PrintServiceLookup.lookupDefaultPrintService();
			
			// Querying for the interface XPrintable on the loaded document
			XPrintable xPrintable = (XPrintable) UnoRuntime.queryInterface(XPrintable.class, xComp);
			
			// Setting the property "Name" for the favoured printer (name of
			// IP address)
			printerDescription[0].Value= printer.getName();
			
			// Setting the name of the printer
			xPrintable.setPrinter(printerDescription);
			
			xPrintable.print(printerOptions);
			
			xComp.dispose();
			
			if (openedHidden)
				closeAndDeleteFile();
		}
		catch (Exception e) 
		{
			logger.log(Level.ERROR, e.getMessage(), e);
		}
	}
	
	
	@Override
	public void saveAsPDF (File pdfFile)
	{
		// TODO Auto-generated method stub
		logger.log(Level.ERROR, "PDF creation not yet supported for OpenOffice");

		PropertyValue[] export2PDFOptions			= new PropertyValue[2];
		export2PDFOptions[0]		= new PropertyValue();
		export2PDFOptions[0].Name	= "URL";
//		export2PDFOptions[0].Value	= "ENTER EXPORT PATH HERE";
		export2PDFOptions[0].Value	= "e:\\test_export.pdf";
		export2PDFOptions[1]		= new PropertyValue();
		export2PDFOptions[1].Name	= "FilterName";
		export2PDFOptions[1].Value	= "writer_pdf_Export";
//		export2PDFOptions[2]		= new PropertyValue();
//		export2PDFOptions[2].Name	= "FilterData";
//		export2PDFOptions[2].Value	= new PropertyValue[]
//				{
//				new PropertyValue("UseLosslessCompression",0,false,com.sun.star.beans.PropertyState.DIRECT_VALUE),
//				new PropertyValue("Quality",0,90,com.sun.star.beans.PropertyState.DIRECT_VALUE),
//				new PropertyValue("ReduceImageResolution",0,false,com.sun.star.beans.PropertyState.DIRECT_VALUE),
//				new PropertyValue("MaxImageResolution",0,300,com.sun.star.beans.PropertyState.DIRECT_VALUE),
//				new PropertyValue("UseTaggedPDF",0,false,com.sun.star.beans.PropertyState.DIRECT_VALUE),
//				new PropertyValue("SelectPdfVersion",0,0,com.sun.star.beans.PropertyState.DIRECT_VALUE),
//				new PropertyValue("ExportNotes",0,false,com.sun.star.beans.PropertyState.DIRECT_VALUE),
//				new PropertyValue("ExportBookmarks",0,true,com.sun.star.beans.PropertyState.DIRECT_VALUE),
//				new PropertyValue("OpenBookmarkLevels",0,-1,com.sun.star.beans.PropertyState.DIRECT_VALUE),
//				new PropertyValue("UseTransitionEffects",0,true,com.sun.star.beans.PropertyState.DIRECT_VALUE),
//				new PropertyValue("IsSkipEmptyPages",0,true,com.sun.star.beans.PropertyState.DIRECT_VALUE),
//				new PropertyValue("IsAddStream",0,false,com.sun.star.beans.PropertyState.DIRECT_VALUE),
//				new PropertyValue("EmbedStandardFonts",0,false,com.sun.star.beans.PropertyState.DIRECT_VALUE),
//				new PropertyValue("FormsType",0,0,com.sun.star.beans.PropertyState.DIRECT_VALUE),
//				new PropertyValue("ExportFormFields",0,true,com.sun.star.beans.PropertyState.DIRECT_VALUE),
//				new PropertyValue("AllowDuplicateFieldNames",0,false,com.sun.star.beans.PropertyState.DIRECT_VALUE),
//				new PropertyValue("HideViewerToolbar",0,false,com.sun.star.beans.PropertyState.DIRECT_VALUE),
//				new PropertyValue("HideViewerMenubar",0,false,com.sun.star.beans.PropertyState.DIRECT_VALUE),
//				new PropertyValue("HideViewerWindowControls",0,false,com.sun.star.beans.PropertyState.DIRECT_VALUE),
//				new PropertyValue("ResizeWindowToInitialPage",0,false,com.sun.star.beans.PropertyState.DIRECT_VALUE),
//				new PropertyValue("CenterWindow",0,false,com.sun.star.beans.PropertyState.DIRECT_VALUE),
//				new PropertyValue("OpenInFullScreenMode",0,false,com.sun.star.beans.PropertyState.DIRECT_VALUE),
//				new PropertyValue("DisplayPDFDocumentTitle",0,true,com.sun.star.beans.PropertyState.DIRECT_VALUE),
//				new PropertyValue("InitialView",0,0,com.sun.star.beans.PropertyState.DIRECT_VALUE),
//				new PropertyValue("Magnification",0,0,com.sun.star.beans.PropertyState.DIRECT_VALUE),
//				new PropertyValue("Zoom",0,100,com.sun.star.beans.PropertyState.DIRECT_VALUE),
//				new PropertyValue("PageLayout",0,0,com.sun.star.beans.PropertyState.DIRECT_VALUE),
//				new PropertyValue("FirstPageOnLeft",0,false,com.sun.star.beans.PropertyState.DIRECT_VALUE),
//				new PropertyValue("InitialPage",0,1,com.sun.star.beans.PropertyState.DIRECT_VALUE),
//				new PropertyValue("Printing",0,2,com.sun.star.beans.PropertyState.DIRECT_VALUE),
//				new PropertyValue("Changes",0,4,com.sun.star.beans.PropertyState.DIRECT_VALUE),
//				new PropertyValue("EnableCopyingOfContent",0,true,com.sun.star.beans.PropertyState.DIRECT_VALUE),
//				new PropertyValue("EnableTextAccessForAccessibilityTools",0,true,com.sun.star.beans.PropertyState.DIRECT_VALUE),
//				new PropertyValue("ExportLinksRelativeFsys",0,false,com.sun.star.beans.PropertyState.DIRECT_VALUE),
//				new PropertyValue("PDFViewSelection",0,0,com.sun.star.beans.PropertyState.DIRECT_VALUE),
//				new PropertyValue("ConvertOOoTargetToPDFTarget",0,false,com.sun.star.beans.PropertyState.DIRECT_VALUE),
//				new PropertyValue("ExportBookmarksToPDFDestination",0,false,com.sun.star.beans.PropertyState.DIRECT_VALUE),
//				new PropertyValue("SignPDF",0,false,com.sun.star.beans.PropertyState.DIRECT_VALUE),
//				new PropertyValue("_OkButtonString",0,"",com.sun.star.beans.PropertyState.DIRECT_VALUE),
//				new PropertyValue("Watermark",0,"",com.sun.star.beans.PropertyState.DIRECT_VALUE),
//				new PropertyValue("EncryptFile",0,false,com.sun.star.beans.PropertyState.DIRECT_VALUE),
//				new PropertyValue("RestrictPermissions",0,false,com.sun.star.beans.PropertyState.DIRECT_VALUE),
//				new PropertyValue("PreparedPermissionPassword",0,new PropertyValue(),com.sun.star.beans.PropertyState.DIRECT_VALUE),
//				new PropertyValue("SignatureLocation",0,"",com.sun.star.beans.PropertyState.DIRECT_VALUE),
//				new PropertyValue("SignatureReason",0,"",com.sun.star.beans.PropertyState.DIRECT_VALUE),
//				new PropertyValue("SignatureContactInfo",0,"",com.sun.star.beans.PropertyState.DIRECT_VALUE),
//				new PropertyValue("SignaturePassword",0,"",com.sun.star.beans.PropertyState.DIRECT_VALUE)
//				};
		dispatchHelper.executeDispatch(dispatchProvider, ".uno:ExportToPDF", "", 0, export2PDFOptions);
		
	}
	
	
	public void close ()
	{
		try
		{
			XTextDocument document = (XTextDocument) UnoRuntime.queryInterface(
					XTextDocument.class, xComp);
			XCloseable closeable = (XCloseable)UnoRuntime.queryInterface(XCloseable.class, document);
			try 
			{
				closeable.close(true);
			}
			catch (CloseVetoException e)
			{
				logger.info("Couldn't close the OO document");
			}
		} 
		catch (Exception e)
		{
			logger.log(Level.ERROR, e.getMessage(), e);
		}
	}
	
	
	@Override
	protected String getDocumentExtension()
	{
		return DOCUMENT_EXTENSION;
	}
	
	
	@Override
	protected InputStream getDefaultTemplate()
	{
		return WriterController.class.getResourceAsStream(DOCUMENT_TEMPLATE_PATH);
	}
	
	
	@Override
	protected boolean isDocumentClosed()
	{
		return closeListener.isBQuit();
	}
	
	
	@Override
	protected int replaceAll (Map<String, String> data)
	{
		XSearchable			searchable;
		XSearchDescriptor	searchDesc;
		XInterface			found;
		XTextRange			textRange;
		XTextCursor			textCursor;
		String				key;
		String				value;
		int					founds	= 0;
		

		searchable	= getSearchable();
		searchDesc	= getSearchDescriptor();
		
		found	= (XInterface) searchable.findFirst(searchDesc);
		while (found != null)
		{
			textRange	= (XTextRange) UnoRuntime.queryInterface(XTextRange.class, found);
			key			= textRange.getString();
			value		= data.get(key);
			if (value != null)
			{
				if (value.toLowerCase().equals("<html></html>")) {
					value = "";
				}
				
				logger.debug("\tReplacing " + key + " with: " + value);
				founds++;
				if (value.startsWith(HTML))
				{
					textCursor = getTextViewCursor();
					textCursor.gotoRange(textRange, false);
					pasteHtml(value);
				}
				else
					textRange.setString(value);
				
				// go to the end of the selection
				textRange	= textRange.getEnd();
			}
			
			// search the text for the next placeholder
			found	= (XInterface) searchable.findNext(found, searchDesc);
		}
		
//		bringDocumentInFront();
		
		return founds;
	}
	
	
	@Override
	protected Collection<String> scanForPlaceholders()
	{
		HashSet<String>		placeholders	= new HashSet<String>();
		XSearchable			searchable;
		XSearchDescriptor	searchDesc;
		XInterface			found;
		XTextRange			textRange;
		String				key;
		
		
		searchable	= getSearchable();
		searchDesc	= getSearchDescriptor();
		searchDesc.setSearchString(super.useNewPlaceholderStyle() ? SEARCH_NEW_STYLE : SEARCH_OLD_STYLE);
		// get the replace / search objects and set the properties
		
		
		found	= (XInterface) searchable.findFirst(searchDesc);
		while (found != null)
		{
			textRange	= (XTextRange) UnoRuntime.queryInterface(XTextRange.class, found);
			key			= textRange.getString();
			placeholders.add(key);
			
			// search the text for the next placeholder
			found	= (XInterface) searchable.findNext(found, searchDesc);
		}
		
//		bringDocumentInFront();
		
		return placeholders;
	}
	
	
	public void checkIfContainsContactInfo ()
	{
		List<String>		placeholders;
		XSearchable			searchable;
		XSearchDescriptor	searchDesc;
		XInterface			found;
		XTextRange			textRange;
		String				key;
		
		
		if (!isTemplate())
			return;
		
		placeholders	= getContactPlaceholders();
		searchable		= getSearchable();
		searchDesc		= getSearchDescriptor();
		searchDesc.setSearchString(useNewPlaceholderStyle() ? SEARCH_NEW_STYLE : SEARCH_OLD_STYLE);
		
		found			= (XInterface) searchable.findFirst(searchDesc);
		while (found != null)
		{
			textRange	= (XTextRange) UnoRuntime.queryInterface(XTextRange.class, found);
			key			= textRange.getString();
			if (placeholders.contains(key))
			{
				containsContactInfo	= Boolean.TRUE;
				return;
			}
			
			// search the text for the next placeholder
			found	= (XInterface) searchable.findNext(found, searchDesc);
		}
		
		containsContactInfo	= Boolean.FALSE;
	}
	
	
	
	/* ======================================== */
	// 		HELP METHODS
	/* ======================================== */
	
	private XTextCursor getTextViewCursor ()
	{
		XTextViewCursorSupplier	textViewCursorSupplier;
		
		
		if (this.textViewCursor == null)
		{
			textViewCursorSupplier		= (XTextViewCursorSupplier)UnoRuntime.queryInterface(
					XTextViewCursorSupplier.class, xController);
			this.textViewCursor		= textViewCursorSupplier.getViewCursor();
		}
		
		return this.textViewCursor;
	}
	
	
	private boolean isOpenOffice()
	{
		return ooInstFolder.toLowerCase().contains("openoffice");
	}
	
	
	// --------------------------------------------------
	// ATTENTION: Frickel alarm!
	// --------------------------------------------------
	
	private void copyToClipboard(String text, String fileEnding)
	{
				
		WriterController	tempWriterController = new WriterController(this.ooInstFolder);
		
		try
		{
			// create a new document as temp file
			tempWriterController.file = File.createTempFile(DOCUMENT_NAME, fileEnding);
			tempWriterController.file = FileUtils.createFile(text.getBytes(), tempWriterController.file);
			// open the document hidden
			tempWriterController.open(DocumentController.TYPE_OTHER, true);
			tempWriterController.copyAll();
		}
		catch (Exception e)
		{
			logger.log(Level.ERROR, e.getMessage(), e);
		}
		finally
		{
			tempWriterController.closeAndDeleteFile();
		}
	}
	
	// ----------   frickel alarm goes on ...  ----------
	
	private void copyAll ()
	{
		XTextCursor		textCursor;
		XWordCursor		wordCursor;
		XTextDocument	document;
		XText			text;
		
		
		textCursor	= getTextViewCursor();
		dispatchHelper.executeDispatch(
				dispatchProvider, ".uno:GoToStartOfDoc", "", 0, null);
		document	= (XTextDocument) UnoRuntime.queryInterface(
				XTextDocument.class, xComp);
		text		= document.getText();
		wordCursor	= (XWordCursor) UnoRuntime.queryInterface(
				XWordCursor.class, text.createTextCursor());
		
		try
		{
			wordCursor.gotoRange(textCursor.getEnd(), false);
			
			dispatchHelper.executeDispatch(
					dispatchProvider, ".uno:SelectAll", "", 0, null);
			dispatchHelper.executeDispatch(
					dispatchProvider, ".uno:Copy", "", 0, null);
		}
		catch (RuntimeException e)
		{
			// The document starts with a table
			dispatchHelper.executeDispatch(
					dispatchProvider, ".uno:GoToStartOfDoc", "", 0, null);
			dispatchHelper.executeDispatch(
					dispatchProvider, ".uno:InsertPara", "", 0, null);
			dispatchHelper.executeDispatch(
					dispatchProvider, ".uno:GoToStartOfDoc", "", 0, null);
			dispatchHelper.executeDispatch(
					dispatchProvider, ".uno:FontHeight", "", 0, minimumFontSizeOptions);
			dispatchHelper.executeDispatch(
					dispatchProvider, ".uno:TopBottomMargin", "", 0, minimumMarginSizeOptions);
			dispatchHelper.executeDispatch(
					dispatchProvider, ".uno:SelectAll", "", 0, null);
			dispatchHelper.executeDispatch(
					dispatchProvider, ".uno:Copy", "", 0, null);
		}
	}
	
	// --------------------------------------------------
	
	// clear System Clipboard
	private void clearClipBoard() {
		try {
			StringSelection stringSelection = new StringSelection("");
			CLIPBOARD.setContents(stringSelection, null);
		} catch (Throwable e) {
		}
	}
	
	private HtmlTransferable getHtmlTransferable (String html)
	{
//		html = html.replaceAll("\\</?html\\>", "");
//		System.out.println(html);
		if (this.transferable == null)
			 this.transferable	= new HtmlTransferable(html);
		else this.transferable.setHtml(html);
		
		return this.transferable;
	}
	
	
	private XSearchable getSearchable ()
	{
		XTextDocument		document;
		
		if (this.searchable == null)
		{
			document 	= (XTextDocument) UnoRuntime.queryInterface(
							XTextDocument.class, xComp);
			searchable	= (XSearchable) UnoRuntime.queryInterface(
							XSearchable.class, document);
		}
		
		return searchable;
	}
	
	
	private XSearchDescriptor getSearchDescriptor ()
	{
		if (this.searchDesc == null)
		{
			searchDesc	= getSearchable().createSearchDescriptor();
			try
			{
				searchDesc.setPropertyValue("SearchAll",				Boolean.TRUE);
				searchDesc.setPropertyValue("SearchBackwards",			Boolean.FALSE);
				searchDesc.setPropertyValue("SearchCaseSensitive",		Boolean.TRUE);
				searchDesc.setPropertyValue("SearchWords",				Boolean.FALSE);
				searchDesc.setPropertyValue("SearchRegularExpression",	Boolean.TRUE);
				searchDesc.setPropertyValue("SearchStyles",				Boolean.FALSE);
				searchDesc.setPropertyValue("SearchSimilarity",			Boolean.FALSE);
				searchDesc.setPropertyValue("SearchSimilarityRelax",	Boolean.FALSE);
//				searchDesc.setPropertyValue("SearchSimilarityRemove",	new Short((short)1));
//				searchDesc.setPropertyValue("SearchSimilarityAdd",		new Short((short)1));
//				searchDesc.setPropertyValue("SearchSimilarityExchange",	new Short((short)1));
			}
			catch (Exception e)
			{
				logger.log(Level.ERROR, e.getMessage(), e);
			}
		}
		searchDesc.setSearchString(super.useNewPlaceholderStyle() ? SEARCH_NEW_STYLE : SEARCH_OLD_STYLE);
		
		return searchDesc;
	}
	
	
	private void bringDocumentInFront()
	{
		XFrame		xFrame		= xController.getFrame();
		XTopWindow	topWindow	= (XTopWindow) UnoRuntime.queryInterface(
								XTopWindow.class, xFrame.getContainerWindow());
		boolean		warn		= false;
		String[]	cmdArgs;
		
		
		// call OO via command line, to bring OO in foreground (the document won't be opened twice)
		try 
		{
			cmdArgs	= getOpenOOCmd();
//			for (String arg : cmdArgs)
//				System.out.print(arg + " ");
//			System.out.println();
			
			if (cmdArgs != null && cmdArgs.length > 0)
				warn = Runtime.getRuntime().exec(cmdArgs).waitFor() > 0;
			else
				warn = true;
		}
		catch (Exception e) 
		{
			warn = true;
			// This is for debugging only. The user doesn't need to see the reason for this error in the log
			e.printStackTrace();
		}
		if (warn) logger.warn("Open Open/LibreOffice via command line failed. The opened document might not come in front");
		
		topWindow.toFront();
		xFrame.activate();
	}
	
	
	private String[] getOpenOOCmd () throws Exception
	{
		String[] openOOCmd;
		
		switch (GECAMedUtils.getOS())
		{
		case GECAMedUtils.OS_MAC:
			openOOCmd		= new String[2]; 
			openOOCmd[0]	= "open";
			openOOCmd[1]	= ooInstFolder.replaceAll("\\.app.*", ".app");
			break;
			
		case GECAMedUtils.OS_LINUX:
			openOOCmd		= new String[2];
			openOOCmd[0]	= ooInstFolder + "/soffice";
			openOOCmd[1]	= file.getCanonicalPath();
			break;
			
		case GECAMedUtils.OS_WINDOWS:
			openOOCmd		= new String[2];
			openOOCmd[0]	= ooInstFolder.replace("/", "\\") + "\\soffice.exe";
			openOOCmd[1]	= file.getCanonicalPath();
			break;
			
		default:
			openOOCmd = new String[0];
		}
		
		return openOOCmd;
	}
	
	
	public static XComponentContext connect (String oooProgramPath) throws Exception
	{
		XComponentContext xContext;
		
		oooProgramPath = FileUtils.adeptInstallPath(oooProgramPath);
		
		// Option 1: use the BootstrapConnector lib
		xContext = BootstrapSocketConnector.bootstrap(oooProgramPath);
		
		// Option 2: Use the self designed BootstrapConnector
//		xContext = OOoSocketConnector.getInstance(oooProgramPath).connect();
		
		// Option 3: Load the OOo program dir into the  classpath, using the UnoClassLoader
//		xContext = connectDirect(oooProgramPath);
		
		if (xContext == null)
			throw new Exception("Error connecting to Open-/LibreOffice.\nProgram dir was: " + oooProgramPath);
		else
			logger.info("XComponentContext received from OOo installation in " + oooProgramPath);
		
		return xContext;
	}
	
	
//	private static XComponentContext connectDirect (String ooProgramPath) throws Exception
//	{
//		File ooProgramDir = new File(ooProgramPath);
//		ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
//		ClassLoader unoClassLoader = new UnoClassLoader(ooProgramDir.toURI().toURL(), new URL[] { ooProgramDir.toURI().toURL() }, classLoader);
//		Thread.currentThread().setContextClassLoader(unoClassLoader);
//		unoClassLoader.loadClass("com.sun.star.comp.helper.BootstrapException");
//		
//		// get the office context 
//		return OOoPipeConnector.bootstrap(ooProgramDir);
//	}
	
	
	
	/* ======================================== */
	// 		MAIN FOR TESTING
	/* ======================================== */
	
	public static void main(String[] args)
	{
		TestUtils.initLogger();
		
		Map<String, String> oldStyleData	= new HashMap<String, String>();
		Map<String, String> newStyleData	= new HashMap<String, String>();
		WriterController	con;
		
		
		oldStyleData.put("hTML",	"<html><h1>DÜFTE NET DA SENN</h1></html>");
		oldStyleData.put("HTML",	"<html><b>headline ...</b><br>... and body</html>");
		oldStyleData.put("TE_XT1",	"Test für Klammernersetzung bestanden");
		oldStyleData.put("tEXt",	"FAILD!");
		oldStyleData.put("TABELLE",	"1\t2\t3\t4\t5\t6");
		oldStyleData.put("HEADER",	"Kopfzeile");
		oldStyleData.put("FOOTER",	"Fußeile");
		
		newStyleData.put("[hTML]",		"<html><h1>DÜFTE NET DA SENN</h1></html>");
//		newStyleData.put("[HTML]",		"<html><h1>Headline ...</h1> ... and body</html>");
		newStyleData.put("[HTML]",		"<html><table><tr><td>dies</td><td>ist</td></tr><tr><td>eine</td><td>Tabelle</td></tr></tabelle></html>");
		newStyleData.put("[HTML1]",		"<html><b>Headline only</b></html>");
		newStyleData.put("[TEXT]",		"Test für Klammernersetzung bestanden");
		newStyleData.put("[TE_XT1]",	"Auch mit Zahlen und _ klappt es!");
		newStyleData.put("[tEXt]",		"FAILD!");
		newStyleData.put("[TEST]",		"Test");
		newStyleData.put("[TABELLE]",	"1\t2\t3\t4\t5\t6");
		newStyleData.put("[HEADER]",	"Kopfzeile");
		newStyleData.put("[FOOTER]",	"Fußeile");
		newStyleData.put("[OFFICE_NAME]", "Demo Office");
		newStyleData.put("[OFFICE_INFORMATIONS]", "This is a medical office");
		newStyleData.put("[OFFICE_FULL_ADDRESS]", "In der Kuhle 1,\n1234 Letzebourg");
		newStyleData.put("[OFFICE_ADDRESS_COUNTRY]", "In der Kuhle 1,\nL-1234 Letzebourg");
		
		
		try 
		{
//			con = new WriterController("C:\\Program Files (x86)\\LibreOffice 4\\program");
			con = new WriterController("C:\\Program Files (x86)\\OpenOffice 4\\program");
//			con.file = new File("c:\\test.odt");
//			con.file = new File("c:\\test.docx");
			con.file = new File("D:\\test.odt");
//			con.file = new File("c:\\test.doc");
//			con.file = new File("c:\\gTest.odt");
			con.setPlaceholderVersion(PH_VERSION_NEW);
//			con.setPlaceholderVersion(PH_VERSION_OLD);
			con.open();
//			for (String p : con.scanForPlaceholders())
//				System.out.println(p);
			con.startReplacing(con.useNewPlaceholderStyle() ? newStyleData : oldStyleData);
			con.waitForUserInput();
//			con.close();
		}
		catch (Exception e) 
		{
			logger.log(Level.ERROR, e.getMessage(), e);
		}
		finally
		{
//			OOoSocketConnector.getInstance(null).disconnect();
			System.exit(0);
		}
	}
	
	
	public static boolean validatePath (String key)
	{
		return new File(key, GECAMedUtils.getOS() == GECAMedUtils.OS_WINDOWS ? "soffice.exe" : "soffice").exists();
	}
	
	
	public static boolean validateFile (File file)
	{
		return new File(file, GECAMedUtils.getOS() == GECAMedUtils.OS_WINDOWS ? "soffice.exe" : "soffice").exists();
	}
}
