/*******************************************************************************
 * 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.letter.gui.dialogs;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.FileFilter;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import java.util.regex.Pattern;

import javax.swing.JFileChooser;
import javax.swing.JPanel;
import javax.swing.JTextField;

import lu.tudor.santec.gecamed.core.gui.GECAMedColors;
import lu.tudor.santec.gecamed.core.gui.MainFrame;
import lu.tudor.santec.gecamed.core.gui.controller.document.writer.WriterController;
import lu.tudor.santec.gecamed.core.gui.widgets.GECAMedBaseDialogImpl;
import lu.tudor.santec.gecamed.core.utils.GECAMedUtils;
import lu.tudor.santec.gecamed.core.utils.controller.utils.FileUtils;
import lu.tudor.santec.i18n.Translatrix;
import lu.tudor.santec.widgets.gui.form.FormInputComponent;
import lu.tudor.santec.widgets.gui.form.FormPanel;

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

/**
 * 
 * Dialog to select the OpenOffice installation path.
 * 
 * @author martin.heinemann@tudor.lu
 * 15.09.2008
 * 11:04:26
 *
 *
 * @version
 * <br>$Log: WriterSelectDialog.java,v $
 * <br>Revision 1.10  2013-12-27 18:09:25  donak
 * <br>Cleanup of imports
 * <br>
 * <br>Revision 1.9  2013-07-15 06:18:36  ferring
 * <br>logging changed
 * <br>
 * <br>Revision 1.8  2012-12-17 14:53:03  ferring
 * <br>file filter option corrected
 * <br>
 * <br>Revision 1.7  2012-12-17 14:36:08  ferring
 * <br>Easier finding the correct OO folder
 * <br>
 * <br>Revision 1.6  2011-08-17 08:41:16  ferring
 * <br>Office controller moved to core as they are not only used by the letter module anymore.
 * <br>
 * <br>Revision 1.5  2008-12-09 15:55:03  hermen
 * <br>added logging
 * <br>
 * <br>Revision 1.4  2008-12-05 17:34:23  heinemann
 * <br>moved some basic classes to lu.tudor.santec.widgets
 * <br>
 * <br>Revision 1.3  2008-11-12 17:16:33  heinemann
 * <br>*** empty log message ***
 * <br>
 * <br>Revision 1.2  2008-09-25 09:43:06  heinemann
 * <br>fixed copyrights
 * <br>
 * <br>Revision 1.1  2008-09-15 12:06:45  heinemann
 * <br>*** empty log message ***
 * <br>
 *   
 */
public class WriterSelectDialog extends GECAMedBaseDialogImpl implements ActionListener{
	
	/* ======================================== */
	// CONSTANTS
	/* ======================================== */
	
	private static final String[]	LINUX_APP_PATHS			= new String[] { "/opt", "/usr/lib" };
//	private static final String		LINUX_OOO_DIR			= "(?i)(open|libre)office(?-i)";
	private static final String		LINUX_OOO_DIR			= "(?i)openoffice(?-i)(.org)?(\\s|\\d|\\.)*";
	private static final String[]	LINUX_OOO_STRUCUTRE		= new String [] { "program", "soffice.bin" };
	
	private static final String[]	MAC_APP_PATHS			= new String[] { "/Applications" };
//	private static final String		MAC_OOO_DIR				= "(?i)(open|libre)office(?-i)(.org)?.app";
	private static final String		MAC_OOO_DIR				= "(?i)openoffice(?-i)(.org)?(\\s|\\d|\\.)*?.app";
	private static final String[]	MAC_OOO_STRUCUTRE		= new String [] { "Contents", "MacOS", "soffice.bin" };
	
	private static final String[]	WINDOWS_APP_PATHS		= new String[] { System.getenv("programfiles"), System.getenv("programfiles(x86)") };
//	private static final String		WINDOWS_OOO_DIR			= "(?i)(open|libre)office(?-i).*";
	private static final String		WINDOWS_OOO_DIR			= "(?i)openoffice(?-i)(.org)?(\\s|\\d|\\.)*";
	private static final String[]	WINDOWS_OOO_STRUCUTRE	= new String [] { "program", "soffice.exe" };
	
	
	
	/* ======================================== */
	// MEMBERS
	/* ======================================== */
	
	/**
	 * static logger for this class
	 */
	private static Logger logger = Logger.getLogger(WriterSelectDialog.class.getName());
	
	private static OOoFileFilter	oooFileFilter	= new OOoFileFilter();
	private static Comparator<File> fileComparator	= new Comparator<File>()
	{
		public int compare (File o1, File o2)
		{
			// sort it from z to a, so that 'o' comes before 'l' and OpenOffice installations will be preferred to LibreOffice installations
			return o1.compareTo(o2) * (-1);
		}
	};
	
	private static final long serialVersionUID = 1L;
	private FormPanel panel;
	private JTextField pathField;
	private FormInputComponent pathInputComponent;
	private String installationPath;
	
	
	
	/* ======================================== */
	// CONSTRUCTORS
	/* ======================================== */
	
	/**
	 * 
	 */
	public WriterSelectDialog() {
		/* ================================================== */
		super(MainFrame.getInstance(),
				Translatrix.getTranslationString("LetterTemplateModule.dialog.selectinstallation.title"),
				OK_CANCEL_BUTTON_MODE,
				new FormPanel());
		
		builPanel();
		/* ================================================== */
	}
	
	
	
	/* ======================================== */
	// CLASS BODY
	/* ======================================== */
	
	public void showDialog() {
		/* ================================================== */
		this.setSize(new Dimension(380, 240));
		findInstallationPath();
		MainFrame.showDialogCentered(this);
		/* ================================================== */
	}
	
	
	/**
	 * 
	 */
	private JPanel builPanel() {
		/* ====================================================== */
		panel = (FormPanel) super.mainPanel;
		panel.setOpaque(false);
		/* ------------------------------------------------------- */
		// set help text
		/* ------------------------------------------------------- */
		panel.addTextBlock(Translatrix.getTranslationString(
							"LetterTemplateModule.dialog.selectinstallation.help"));
		/* ------------------------------------------------------- */
		
		panel.addGap();
		panel.addGap();
		/* ------------------------------------------------------- */
		pathField = new JTextField(35);
		pathField.setEditable(false);
		/* ------------------------------------------------------- */
		pathInputComponent = new FormInputComponent(
							"LetterTemplateModule.dialog.selectinstallation.path",
							this.pathField, 
							"...");
		pathInputComponent.addActionListener(this);
		pathInputComponent.setOpaque(false);
		/* ------------------------------------------------------- */
		panel.addLine(pathInputComponent);
		panel.addGap();
		panel.addGap();
		/* ------------------------------------------------------- */
		super.okButton.setEnabled(false);
		/* ------------------------------------------------------- */
		
//		this.okButton.addActionListener(	this);
//		this.cancelButton.addActionListener(this);
		
		return panel;
		/* ====================================================== */
	}
	
	
	@Override
	public void cancelActionCalled ()
	{
		installationPath = null;
		super.cancelActionCalled();
	}
	

	/* (non-Javadoc)
	 * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
	 */
	public void actionPerformed(ActionEvent e) {
		/* ====================================================== */
		File selectedFile;
//		File[] filteredFiles;
		
		if (this.pathInputComponent.equals(e.getSource())) {
			/* ------------------------------------------------------- */
			// show the chooser
			/* ------------------------------------------------------- */
			JFileChooser chooser = MainFrame.getFileChooser();
			// configure chooser
			chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
			/* ------------------------------------------------------- */
			int returnVal = chooser.showOpenDialog(this);
			/* ------------------------------------------------------- */
			if (returnVal == JFileChooser.APPROVE_OPTION) {
				/* ------------------------------------------------------- */
				selectedFile	= findInstallationPath(chooser.getSelectedFile());
				
				if (selectedFile == null)
					selectedFile= chooser.getSelectedFile();
				if (!validatePath(selectedFile) 
						&& !chooser.getSelectedFile().equals(selectedFile))
					validatePath(chooser.getSelectedFile());
				/* ------------------------------------------------------- */
			}
			/* ------------------------------------------------------- */
		}
		/* ------------------------------------------------------- */
//		else if (this.okButton.equals(e.getSource())) {
//			/* ------------------------------------------------------- */
//			this.setVisible(false);
//			/* ------------------------------------------------------- */
//		}
//		else if (this.cancelButton.equals(e.getSource())) {
//			/* ------------------------------------------------------- */
//			this.installationPath = null;
//			this.setVisible(false);
//			/* ------------------------------------------------------- */
//		}
		/* ====================================================== */
	}
	
	
	private boolean validatePath (File file)
	{
//		try {
			/* ------------------------------------------------------- */
			// test the selected installation path
			// will throw an exception if something went wrong
			/* ------------------------------------------------------- */
//			BootstrapSocketConnector.bootstrap(FileUtils.adeptInstallPath(file.getPath()));
		if (WriterController.validateFile(file))
		{
			/* ------------------------------------------------------- */
			// save an adapted path. \ will be replaced by /
			/* ------------------------------------------------------- */
			this.installationPath = FileUtils.adeptInstallPath(file.getPath());
			/* ------------------------------------------------------- */
			// show the original path.
			/* ------------------------------------------------------- */
			this.pathField.setText(file.getPath());
			
			this.pathField.setBackground(new Color(206, 241, 188));
			this.okButton.setEnabled(true);
			
			return true;
			/* ------------------------------------------------------- */
		} 
//		catch (Throwable ex)
		else
		{
			/* ------------------------------------------------------- */
//		    logger.log(Level.WARN, "No working Openoffice installation found in " + file.getPath(), ex);
			logger.log(Level.WARN, "No working Openoffice installation found in " + file.getPath());
			this.pathField.setText(file.getPath());
			
			this.pathField.setBackground(GECAMedColors.c_InvalidFieldBackground);
			this.okButton.setEnabled(false);
//			this.cancelButton.setEnabled(true);
			
			return false;
			/* ------------------------------------------------------- */
		}
	}
	

	/**
	 * @return the installationPath
	 */
	public String getInstallationPath() {
		/* ====================================================== */
		return installationPath;
		/* ====================================================== */
		}
	
	
	private void findInstallationPath ()
	{
		File installDir	= null;
		if (installationPath == null)
		{
			for (String appPath : oooFileFilter.getAppPaths())
			{
				if (installDir != null)
					break;
				else
					installDir	= findInstallationPath(new File(appPath));
			}
		}
		else
		{
			installDir	= findInstallationPath(new File(installationPath));
		}
		

		if (installDir != null)
			validatePath(installDir);
		else
			logger.info("Unable to find the default OOo install dir.");
	}
	
	
	private File findInstallationPath (File dir)
	{
		File		foundFile;
		Set<File>	filteredFiles	= new TreeSet<File>(fileComparator);
				
		for (File file : dir.listFiles(oooFileFilter))
			filteredFiles.add(file);
		
		for (File file : filteredFiles)
		{
			if (oooFileFilter.validateDir(file))
				return file;
			else
			{
				foundFile	= findInstallationPath(file);
				if (foundFile != null)
					return foundFile;
			}
		}
		
		return null;
	}
	
	
	
	/* ======================================== */
	// CLASS: OOoFileFilter
	/* ======================================== */
	
	private static class OOoFileFilter implements FileFilter
	{
		private Set<String>		appPaths;
		private Pattern			oooDirPattern;
		private String			executable;
		private List<String>	oooStructure;
		private	FileFilter		executableFilter;
		
		
		public OOoFileFilter ()
		{
			String[]	structure;
			String[]	paths;
			
			switch (GECAMedUtils.getOS())
			{
				case GECAMedUtils.OS_LINUX:
					paths		= LINUX_APP_PATHS;
					oooDirPattern	= Pattern.compile(LINUX_OOO_DIR);
					structure		= LINUX_OOO_STRUCUTRE;
					break;

				case GECAMedUtils.OS_MAC:
					paths		= MAC_APP_PATHS;
					oooDirPattern	= Pattern.compile(MAC_OOO_DIR);
					structure		= MAC_OOO_STRUCUTRE;
					break;

				case GECAMedUtils.OS_WINDOWS:
					paths		= WINDOWS_APP_PATHS;
					oooDirPattern	= Pattern.compile(WINDOWS_OOO_DIR);
					structure		= WINDOWS_OOO_STRUCUTRE;
					break;
					
				default:
					throw new RuntimeException("Unknown or unsupported OS");
			}
			
			appPaths				= new HashSet<String>(paths.length);
			for (String path : paths)
				if (path != null)
					appPaths.add(path);
			
			oooStructure		= new ArrayList<String>(structure.length-1);
			int index;
			for (index = 0; index < structure.length-1; index++)
				oooStructure.add(structure[index]);
			
			executable			= structure[index];
			
			executableFilter	= new FileFilter()
			{
				public boolean accept (File file)
				{
					return !file.isDirectory() 
							&& file.getName().equals(executable);
				}
			};
		}
		
		
		public Set<String> getAppPaths ()
		{
			return appPaths;
		}
		
		
//		public String getExecutable ()
//		{
//			return executable;
//		}
		
		
		public boolean accept (File file)
		{
			if (!file.isDirectory())
				return false;
			
			return appPaths.contains(file.getAbsolutePath())
					|| oooDirPattern.matcher(file.getName()).matches()
					|| oooStructure.contains(file.getName());
		}
		
		
		public boolean validateDir (File file)
		{
			if (!file.isDirectory()
					|| file.listFiles(executableFilter).length == 0)
				return false;
			
			for (int index = oooStructure.size()-1; index >= 0; index--)
			{
				if (!file.getName().equals(oooStructure.get(index)))
					return false;
				
				file	= file.getParentFile();
			}
			
			return true;
		}
	}
}
