//------------------------------------------------------------------------------
// Copyright (c) 2005, 2006 IBM Corporation and others.
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// which accompanies this distribution, and is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// Contributors:
// IBM Corporation - initial implementation
//------------------------------------------------------------------------------
package org.eclipse.epf.library.ui;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.emf.common.CommonPlugin;
import org.eclipse.epf.common.serviceability.MsgDialog;
import org.eclipse.epf.common.serviceability.VersionUtil;
import org.eclipse.epf.library.LibraryService;
import org.eclipse.epf.library.services.SafeUpdateController;
import org.eclipse.epf.library.ui.actions.ConfigurationContributionItem;
import org.eclipse.epf.library.ui.dialogs.OpenLibraryDialog;
import org.eclipse.epf.library.ui.dialogs.SelectLibraryDirectoryDialog;
import org.eclipse.epf.library.ui.preferences.LibraryUIPreferences;
import org.eclipse.epf.library.ui.wizards.LibraryBackupUtil;
import org.eclipse.epf.library.xmi.XMILibraryManager;
import org.eclipse.epf.library.xmi.XMILibraryUtil;
import org.eclipse.epf.persistence.MultiFileResourceSetImpl;
import org.eclipse.epf.persistence.MultiFileSaveUtil;
import org.eclipse.epf.persistence.migration.MappingUtil;
import org.eclipse.epf.uma.MethodLibrary;
import org.eclipse.jface.action.IContributionItem;
import org.eclipse.jface.action.ICoolBarManager;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.action.ToolBarContributionItem;
import org.eclipse.jface.action.ToolBarManager;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.dialogs.ProgressMonitorDialog;
import org.eclipse.jface.window.ApplicationWindow;
import org.eclipse.jface.window.Window;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.actions.WorkspaceModifyOperation;

/**
 * The Library UI Manager.
 * 
 * @author Kelvin Low
 * @author Phong Nguyen Le
 * @since 1.0
 */
public class LibraryUIManager {

	private static final String TOOLBAR_CONFIG_CONTRIBUTION_ID = "toolbar.config.contribution"; //$NON-NLS-1$

	private static LibraryUIManager instance = null;

	private static String cmdLineLibPath = null;
	private static String cmdLineDefaultLibPath = null;
	
	private static String appName = "composer";

	/**
	 * Returns the singleton instance.
	 */
	public static LibraryUIManager getInstance() {
		if (instance == null) {
			synchronized (LibraryUIManager.class) {
				if (instance == null) {
					instance = new LibraryUIManager();
				}
			}
		}
		return instance;
	}

	/**
	 * Private default constructor to prevent this class from being
	 * instantiated.
	 */
	private LibraryUIManager() {
		addConfigurationContribution();
	}

	/**
	 * Prompts the user to select a Method Library.
	 */
	public void promptForMethodLibrary() {
		// Retrieve the Library path that was saved in a previous session.
		String libPath = LibraryUIPreferences.getSavedLibraryPath();
		if (cmdLineLibPath != null) {
			libPath = cmdLineLibPath;
		} else if (libPath == null || libPath.length() == 0) {
			if (cmdLineDefaultLibPath != null) {
				libPath = cmdLineDefaultLibPath;
			}
		}

		// Prompt the user to select a Method Library folder if:
		// (1) The saved or command line Library path is invalid AND
		// (2) The prompt for Method Library at startup preference is set to
		// true (the default)
		if (XMILibraryUtil.isValidLibrary(libPath, true) == Status.OK_STATUS
				&& !LibraryUIPreferences.getPromptForMethodLibraryAtStartup()
				&& !XMILibraryUtil.isMethodLibraryLocked(libPath)) {
			openLibrary(libPath);
			return;
		}

		// Open a dialog to prompt the user to select a Method Librray folder.
		if (XMILibraryUtil.isValidLibrary(libPath) != Status.OK_STATUS) {
			libPath = LibraryUIPreferences.getDefaultLibraryPath();
		}
		
		// bug 151199 - change parent shell to null. dialog is also changed to
		// not be ON_TOP
		OpenLibraryDialog dialog = new OpenLibraryDialog(null, libPath);
		while (dialog.open() == Window.OK) {
			libPath = dialog.getLibraryPath();
			libPath = toAbsoluteLibraryPath(libPath);
			if (XMILibraryUtil.isValidLibrary(libPath, true) == Status.OK_STATUS) {
				if (XMILibraryUtil.isMethodLibraryLocked(libPath)) {
					if (displayLibraryLockedMessage() != 0)
						continue;
				}
				VersionUtil.VersionCheckInfo info = VersionUtil.checkLibraryVersion(
						new File(libPath, XMILibraryManager.LIBRARY_XMI),
						VersionUtil.FILETYPE_XMI);
				if (info != null && info.result > 0) {
					String message = ""; //$NON-NLS-1$
					if (info.toolID.equals(VersionUtil.getPrimaryToolID())) {
						message = NLS.bind(LibraryUIResources.versionMismatchDialog_text, new Object[] {
								Platform.getProduct().getName(), info.toolVersion});
					} else {
						message = NLS.bind(LibraryUIResources.versionMismatchDialog_text_unknown, new Object[] {
								Platform.getProduct().getName()});
					}
					LibraryUIPlugin.getDefault().getMsgDialog().
						displayError(LibraryUIResources.openLibraryDialog_title, message);
					continue;
				}
				if (XMILibraryUtil.isMethodLibraryUpgradeRequired(libPath)) {
					if (!LibraryUIPlugin
							.getDefault()
							.getMsgDialog()
							.displayConfirmation(
									LibraryUIResources.openLibraryDialog_title,
									LibraryUIResources.upgradeLibraryDialog_text)) {
						continue;
					}
					if (!upgradeLibrary(libPath)) {
						continue;
					}
				}
				if (openLibrary(libPath)) {
					return;
				}
			} else {
				MsgDialog msgDialog = LibraryUIPlugin.getDefault()
						.getMsgDialog();
				boolean rc = msgDialog
						.displayConfirmation(
								LibraryUIResources.openLibraryDialog_title,
								NLS
										.bind(
												LibraryUIResources.openLibraryDialog_newLibrary_text,
												new Object[] { libPath }));
				if (!rc)
					continue;
				if (createLibrary(libPath)) {
					return;
				}
			}

			LibraryUIPlugin.getDefault().getMsgDialog().displayError(
					LibraryUIResources.openLibraryDialog_title,
					LibraryUIResources.invalidLibraryPath_msg,
					LibraryUIResources.invalidLibraryPath_reason);
		}
		;

		// if dialog is closed with the OK buttonbeing clicked -- treat it as
		// cancel
		System.exit(0);
	}

	/**
	 * Opens a Method Library.
	 */
	public void openLibrary() {
		SelectLibraryDirectoryDialog dialog = new SelectLibraryDirectoryDialog(
				Display.getCurrent().getActiveShell());
		String libPath = LibraryUIPreferences.getSavedLibraryPath();
		dialog.setFilterPath(libPath);
		do {
			libPath = dialog.open();
			if (libPath == null) {
				return;
			}
			if (XMILibraryUtil.isValidLibrary(libPath, true) == Status.OK_STATUS) {
				if (XMILibraryUtil.isMethodLibraryLocked(libPath)) {
					if (displayLibraryLockedMessage() != 0)
						continue;
				}
				VersionUtil.VersionCheckInfo info = VersionUtil.checkLibraryVersion(
						new File(libPath, XMILibraryManager.LIBRARY_XMI),
						VersionUtil.FILETYPE_XMI);
				if (info != null && info.result > 0) {
					String message = ""; //$NON-NLS-1$
					if (info.toolID.equals(VersionUtil.getPrimaryToolID())) {
						message = NLS.bind(LibraryUIResources.versionMismatchDialog_text, new Object[] {
								Platform.getProduct().getName(), info.toolVersion});
					} else {
						message = NLS.bind(LibraryUIResources.versionMismatchDialog_text_unknown, new Object[] {
								Platform.getProduct().getName()});
					}
					LibraryUIPlugin.getDefault().getMsgDialog().
						displayError(LibraryUIResources.openLibraryDialog_title, message);
					return;
				}
				if (XMILibraryUtil.isMethodLibraryUpgradeRequired(libPath)) {
					if (!LibraryUIPlugin
							.getDefault()
							.getMsgDialog()
							.displayConfirmation(
									LibraryUIResources.openLibraryDialog_title,
									LibraryUIResources.upgradeLibraryDialog_text)) {
						return;
					}
					if (!upgradeLibrary(libPath)) {
						return;
					}
				}
				if (openLibrary(libPath)) {
					return;
				}
			}
			MsgDialog msgDialog = LibraryUIPlugin.getDefault().getMsgDialog();
			msgDialog.displayError(LibraryUIResources.openLibraryDialog_title,
					LibraryUIResources.invalidLibraryPath_msg,
					LibraryUIResources.invalidLibraryPath_reason);
		} while (libPath != null);
	}

	/**
	 * Opens a Method Library given the Library path.
	 * 
	 * @param path
	 *            Path to a Method Library.
	 * @return <code>true</code> if the Method Library is opened successfully.
	 */
	public boolean openLibrary(final String path) {
		Shell shell = Display.getCurrent().getActiveShell();

		final List errors = new ArrayList();
		// final IStatus[] status = new IStatus[1];

		// Do the work within an operation because this is a long running
		// activity that modifies the workspace.
		WorkspaceModifyOperation operation = new WorkspaceModifyOperation() {
			public void execute(IProgressMonitor monitor) {
				String taskName = LibraryUIResources.openingLibraryTask_name;
				monitor.beginTask(taskName, 2);
				try {
					monitor.setTaskName(taskName);
					monitor.worked(1);
					LibraryService.getInstance().closeCurrentMethodLibrary();
					MethodLibrary library = XMILibraryUtil
							.openMethodLibrary(path);
					LibraryService.getInstance().setCurrentMethodLibrary(
							library);
					LibraryUIPreferences.setSavedLibraryPath(path);
					// show Problems View if necessary
					MultiFileResourceSetImpl resourceSet = ((MultiFileResourceSetImpl) LibraryService
							.getInstance().getCurrentLibraryManager()
							.getEditingDomain().getResourceSet());
					if (resourceSet.getMarkerMananger().hasUnresolvedProxy()) {
						SafeUpdateController.asyncExec(new Runnable() {
							public void run() {
								try {
									PlatformUI
											.getWorkbench()
											.getActiveWorkbenchWindow()
											.getActivePage()
											.showView(
													"org.eclipse.ui.views.ProblemView", null, IWorkbenchPage.VIEW_VISIBLE); //$NON-NLS-1$
								} catch (Exception e) {
									// couldn't open the problem view, too bad..
								}
							}
						});
					}
				} catch (Exception e) {
					if (!(e instanceof IOException && e.getMessage()
							.startsWith("###"))) { //$NON-NLS-1$
						LibraryUIPlugin.getDefault().getLogger().logError(e);
					}
					errors.add(e);
				} finally {
					monitor.done();
				}
			}
		};

		try {
			ProgressMonitorDialog dialog = new ProgressMonitorDialog(shell) {
				protected void configureShell(Shell shell) {
					super.configureShell(shell);
					shell.setText(LibraryUIResources.openLibraryDialog_title);
				}
			};
			dialog.run(true, false, operation);
			if (errors.isEmpty()) {
				IWorkbenchWindow workbenchWindow = PlatformUI.getWorkbench()
						.getActiveWorkbenchWindow();
				if (workbenchWindow != null) {
					IWorkbenchPage activePage = workbenchWindow.getActivePage();
					if (activePage != null) {
						activePage.closeAllEditors(false);
					}
				}
				return true;
			} else {
				Iterator iter = errors.iterator();
				while (iter.hasNext()) {
					Exception e = (Exception) iter.next();
					if (e instanceof IOException) {
						String message = e.getMessage();
						if (message.startsWith("###")) { //$NON-NLS-1$
							String projectFileName = message.substring(3);

							String prompt = LibraryUIResources
									.bind(
											LibraryUIResources.readOnlyProjectFile_text,
											projectFileName);
							String[] buttonLabels = {
									LibraryUIResources.retryButton_text,
									LibraryUIResources.cancelButton_text };
							MessageDialog msgBox = new MessageDialog(Display
									.getCurrent().getActiveShell(),
									LibraryUIResources.openLibraryDialog_title,
									null, prompt, MessageDialog.WARNING,
									buttonLabels, 0);
							if (msgBox.open() == 0) {
								return openLibrary(path);
							} else {
								return true;
							}
						}
					}
				}
			}
		} catch (Exception e) {
			LibraryUIPlugin.getDefault().getLogger().logError(e);
		}

		return false;
	}

	/**
	 * Creates and opens a new Method Library.
	 * 
	 * @param path
	 *            The Method Library path.
	 * @return <code>true</code> if the Method Library is opened successfully.
	 */
	public boolean createLibrary(String path) {
		try {
			File libraryPath = new File(path);
			if (!libraryPath.exists()) {
				libraryPath.mkdirs();
			}
			XMILibraryUtil.createMethodLibrary(libraryPath.getName(), path);
			LibraryUIPreferences.setSavedLibraryPath(path);
			return true;
		} catch (Exception e) {
			return false;
		}
	}

	/**
	 * Adds the Configuration contribution to the workbench toolbar.
	 */
	public void addConfigurationContribution() {
		IWorkbench workbench = LibraryUIPlugin.getDefault().getWorkbench();
		if (workbench != null) {
			IWorkbenchWindow window = (IWorkbenchWindow) workbench
					.getActiveWorkbenchWindow();
			if (window != null && window instanceof ApplicationWindow) {
				// Check to see if the configuration contribution already
				// exists.
				ICoolBarManager coolBar = ((ApplicationWindow)window).getCoolBarManager();
				IContributionItem marker = null;
				IContributionItem coolBarItem = coolBar
						.find(TOOLBAR_CONFIG_CONTRIBUTION_ID);
				if (coolBarItem != null) {
					if (coolBarItem.isVisible()) {
						return;
					}
					IContributionItem[] items = coolBar.getItems();
					for (int i = 0; i < items.length; i++) {
						if (items[i] == coolBarItem) {
							coolBar.remove(TOOLBAR_CONFIG_CONTRIBUTION_ID);
							if (i + 1 < items.length) {
								marker = items[i + 1];
							}
						}
					}
				}

				IToolBarManager toolbarMgr = new ToolBarManager(SWT.FLAT
						| SWT.LEFT);
				ConfigurationContributionItem testItem = new ConfigurationContributionItem(
						null);
				toolbarMgr.add(testItem);
				ToolBarContributionItem contribItem = new ToolBarContributionItem(
						toolbarMgr, TOOLBAR_CONFIG_CONTRIBUTION_ID);
				if (marker != null) {
					coolBar.insertBefore(marker.getId(), contribItem);
				} else {
					coolBar.add(contribItem);
				}
			}
		}
	}

	/**
	 * Upgrades a Method Library to a new meta-model.
	 * 
	 * @param path
	 *            Path to a Method Library.
	 * @return <code>true</code> if the given Method Library is sucessfully
	 *         upgraded.
	 */
	public static boolean upgradeLibrary(final String libDir) {								
		Shell shell = Display.getCurrent().getActiveShell();
		LibraryBackupUtil.promptBackupLibrary(shell, new File(libDir));
		
		final StringBuffer errMsg = new StringBuffer();
		final boolean[] cancelFlagHolder = { false };

		// Do the work within an operation because this is a long running
		// activity that modifies the workbench.
		WorkspaceModifyOperation operation = new WorkspaceModifyOperation() {
			public void execute(IProgressMonitor monitor) {
				monitor.beginTask(LibraryUIResources.upgradingLibraryTask_name, 10);
				monitor.worked(1);
				try {
					MappingUtil
							.migrate(
									libDir
											+ "/" + MultiFileSaveUtil.DEFAULT_LIBRARY_MODEL_FILENAME, monitor); //$NON-NLS-1$
				} 
				catch(OperationCanceledException e) {
					cancelFlagHolder[0] = true;
				}
				catch (Exception e) {
					CommonPlugin.INSTANCE.log(e);
					e.printStackTrace();
					String msg = e.getMessage();
					if (msg == null) {
						msg = LibraryUIResources.upgradeLibraryError_msg;
					}
					errMsg.append(msg);
				} finally {
					monitor.done();
				}
			}
		};

		try {
			// Run the operation and display the progress.
			new ProgressMonitorDialog(shell).run(true, false, operation);
			if(cancelFlagHolder[0]) {
				return false;
			}
			else if (errMsg.length() > 0) {
				LibraryUIPlugin.getDefault().getMsgDialog().displayError(
						LibraryUIResources.upgradeLibraryDialog_title,
						errMsg.toString());
				return false;
			}
			return true;
		} catch (Exception e) {
			LibraryUIPlugin.getDefault().getLogger().logError(e);
			e.printStackTrace();
			LibraryUIPlugin.getDefault().getMsgDialog().displayError(
					LibraryUIResources.upgradeLibraryDialog_title,
					LibraryUIResources.upgradeLibraryError_msg);
			return false;
		}
	}

	/**
	 * Returns the Library path set via the command line option "-library
	 * <path>".
	 */
	public static String getCommandLineLibraryPath() {
		return cmdLineLibPath;
	}

	/**
	 * Sets the Library path via the command line option "-library <path>".
	 */
	public static void setCommandLineLibraryPath(String libPath) {
		cmdLineLibPath = libPath;
		IPath path = Path.fromOSString(libPath);
		if (!path.isAbsolute()) {
			// TODO: Review implementation.
			cmdLineLibPath = System.getProperty("user.dir") + File.separator + libPath; //$NON-NLS-1$
		}
		if (XMILibraryUtil.isValidLibrary(cmdLineLibPath, true) != Status.OK_STATUS) {
			cmdLineLibPath = null;
		}
	}

	/**
	 * Sets the Library path via the command line option "-defaultlibrary <path>".
	 */
	public static void setCommandLineDefaultLibraryPath(String libPath) {
		IPath path = Path.fromOSString(libPath);
		if (!path.isAbsolute()) {
			// TODO: Review implementation.
			cmdLineDefaultLibPath = System.getProperty("user.dir") + File.separator + libPath; //$NON-NLS-1$
		}
		if (XMILibraryUtil.isValidLibrary(cmdLineDefaultLibPath, true) != Status.OK_STATUS) {
			cmdLineDefaultLibPath = null;
		}
	}
	
	public static String toAbsoluteLibraryPath(String libPath) {
		String absPath = libPath;

		IPath ecPath = Path.fromOSString(libPath);
		if (!ecPath.isAbsolute()) {
			// TODO: Review implementation.
			absPath = System.getProperty("user.dir") + File.separator + //$NON-NLS-1$
					"Method Libraries" + File.separator + libPath; //$NON-NLS-1$
		}

		return absPath;
	}

	private static int displayLibraryLockedMessage() {
		Shell shell = null;
		Image image = null;
		try {
			shell = LibraryUIPlugin.getDefault().getWorkbench().getDisplay()
					.getActiveShell();
			image = shell.getImage();
		} catch (Exception e) {
		}
		MessageDialog msgDlg = new MessageDialog(shell,
				LibraryUIResources.openLibraryDialog_title, image,
				LibraryUIResources.libraryLocked_msg, MessageDialog.ERROR,
				new String[] {
						LibraryUIResources.libraryLocked_openButton_text,
						IDialogConstants.CANCEL_LABEL },
				1);

		return msgDlg.open();
	}

	public static String getAppName() {
		return appName;
	}

	public static void setAppName(String appName) {
		LibraryUIManager.appName = appName;
	}
}
