//------------------------------------------------------------------------------
// 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.xmi;

import java.io.File;
import java.util.HashMap;
import java.util.Map;

import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IProjectDescription;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.epf.common.utils.FileUtil;
import org.eclipse.epf.common.utils.StrUtil;
import org.eclipse.epf.library.LibraryPlugin;
import org.eclipse.epf.library.LibraryResources;
import org.eclipse.epf.library.LibraryService;
import org.eclipse.epf.library.LibraryServiceException;
import org.eclipse.epf.library.util.ResourceUtil;
import org.eclipse.epf.persistence.MultiFileResourceSetImpl;
import org.eclipse.epf.persistence.MultiFileSaveUtil;
import org.eclipse.epf.persistence.util.PersistenceUtil;
import org.eclipse.epf.uma.MethodLibrary;

/**
 * Helper utilities for accessing a XMI method library.
 * 
 * @author Kelvin Low
 * @since 1.0
 */
public class XMILibraryUtil {

	private static final String EMPTY_METHOD_LIBRARY_PATH_MSG = LibraryResources.emptyLibraryPathError_reason;

	private static final String MISSING_LIBRARY_FILE_MSG = LibraryResources.missingLibraryFileError_reason;

	private static final String COULD_NOT_LOCK_LIBRARY_MSG = LibraryResources.couldNotLockLibrary_reason;

	public static final IStatus EMPTY_METHOD_LIBRARY_PATH_STATUS = new Status(
			Status.ERROR, LibraryPlugin.getDefault().getId(), 1,
			EMPTY_METHOD_LIBRARY_PATH_MSG, null);

	public static final IStatus MISSING_LIBRARY_FILE_STATUS = new Status(
			Status.ERROR, LibraryPlugin.getDefault().getId(), 1,
			MISSING_LIBRARY_FILE_MSG, null);

	public static final IStatus COULD_NOT_LOCK_LIBRARY_STATUS = new Status(
			Status.ERROR, LibraryPlugin.getDefault().getId(), 1,
			COULD_NOT_LOCK_LIBRARY_MSG, null);

	static {
		try {
			// TODO: This call is needed to initialize the persistence layer
			// to avoid a null pointer exception in
			// PersistenceUtil.conversionRequired(). A better solution is
			// needed.
			new MultiFileResourceSetImpl();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * Checks whether a method library exists on a given path.
	 * 
	 * @param path
	 *            an absolute path to a method library
	 * @return <code>true</code> if the given path contains a valid method
	 *         library
	 */
	public static boolean hasMethodLibrary(String path) {
		if (path == null) {
			throw new IllegalArgumentException();
		}

		File libraryDir = new File(path);
		if (!libraryDir.exists()) {
			return false;
		}

		File libraryFile = new File(libraryDir, XMILibraryManager.LIBRARY_XMI);
		if (!libraryFile.exists()) {
			return false;
		}

		return true;
	}

	/**
	 * Returns the absolute path to a method library model (library.xmi) file.
	 * 
	 * @param path
	 *            an absolute path to a method library
	 * @returnt an absolute path to a method library model file (if one exists)
	 */
	public static String getLibraryModelFile(String path) {
		if (!StrUtil.isBlank(path)) {
			String libPath = new File(path).getAbsolutePath();
			String suffix = File.separator
					+ MultiFileSaveUtil.DEFAULT_LIBRARY_MODEL_FILENAME;
			if (libPath.endsWith(suffix)) {
				return libPath;
			} else {
				return libPath + suffix;
			}
		}
		return path;
	}

	/**
	 * Validates a method library path.
	 * 
	 * @param path
	 *            an absolute path to a method library
	 * @param validateModelFile
	 *            if <code>true</code>, validate that the library.xmi file
	 *            also exists.
	 * @return <code>true</code> if the given path is a valid method library
	 *         folder
	 */
	public static IStatus isValidLibrary(String path, boolean validateModelFile) {
		if (path == null || path.trim().length() == 0) {
			return EMPTY_METHOD_LIBRARY_PATH_STATUS;
		}

		if (validateModelFile) {
			File libFile = new File(getLibraryModelFile(path));
			if (!libFile.exists()) {
				return MISSING_LIBRARY_FILE_STATUS;
			}
		}

		File libPath = new File(path);
		if (path.endsWith(MultiFileSaveUtil.DEFAULT_LIBRARY_MODEL_FILENAME)) {
			libPath = libPath.getParentFile();
		}

		String libFullPath = libPath.getAbsolutePath();

		if (libFullPath.startsWith("\\")) { //$NON-NLS-1$
			// the first part must be a machine name, and you must specify a
			// folder

			boolean isValid = true;
			String uncPath;
			int index = libFullPath.indexOf(File.separatorChar, 2);
			if (index < 0) {
				isValid = false;
			} else {
				uncPath = libFullPath.substring(index + 1);
				if (uncPath.length() == 0) {
					// must specify a valid path
					isValid = false;
				} else {
					// the top level path must be valid
					index = libFullPath.indexOf(File.separatorChar, index + 1);
					if (index < 0) {
						uncPath = libFullPath;
					} else {
						uncPath = libFullPath.substring(0, index);
					}

					File f = new File(uncPath);
					if (!f.exists()) {
						isValid = false;
					}
				}
			}

			if (!isValid) {
				// no new resource string, wait later
				// return new Status(Status.ERROR,
				// LibraryPlugin.getDefault().getId(), 1, "Invalid path", null);
				return EMPTY_METHOD_LIBRARY_PATH_STATUS;
			}
		}

		// Check for presence of a .project file.
		IProjectDescription description = null;
		try {
			description = ResourcesPlugin
					.getWorkspace()
					.loadProjectDescription(
							new Path(libFullPath + File.separator
									+ IProjectDescription.DESCRIPTION_FILE_NAME));
			if (description != null) {
				return Status.OK_STATUS;
			}
		} catch (CoreException e) {
		}

		// If a .project file does not exists, use it to validate the path.
		// only call validateProjectLocation if path is not on workspace
		// for some reason, validateProjectLocation only works on paths not on the workspace
		if (Platform.getLocation().isPrefixOf(new Path(libFullPath))) {
			return Status.OK_STATUS;
		}
		IProject project = ResourceUtil.findProject(libFullPath);
		if (project == null) {
			project = ResourcesPlugin.getWorkspace().getRoot().getProject(
					libPath.getName());
		}

		return ResourcesPlugin.getWorkspace().validateProjectLocation(project,
				new Path(libFullPath));
	}

	/**
	 * Validates a method library path.
	 * 
	 * @param path
	 *            an absolute path to a method library
	 * @return <code>true</code> if the given path is a valid method library
	 *         folder
	 */
	public static IStatus isValidLibrary(String path) {
		return isValidLibrary(path, false);
	}

	/**
	 * Checks whether a method library is locked.
	 * <p>
	 * Once a method library is locked, it cannot be accessed by another EPF
	 * Composer instance.
	 * 
	 * @param path
	 *            an absolute path to a method library
	 * @return <code>true</code> if the method library is locked
	 */
	public static boolean isMethodLibraryLocked(String path) {
		// check if library is currently opened by this instance
		// the FileLock mechanism in Java does not seem to properly detect when
		// 		the current JVM has locked the file
		// TODO: check all opened libraries (rather than just current) when the API allows it
		String currentLibraryPath = LibraryService.getInstance().getCurrentMethodLibraryPath();
		if (currentLibraryPath != null && new File(currentLibraryPath).equals(new File(path)))
			return false;
		File lockFile = new File(path, ResourceUtil.LOCK_FILENAME);
		return FileUtil.isFileLocked(lockFile);
	}

	/**
	 * Checks whether a method library needs to be upgraded to a new format.
	 * 
	 * @param path
	 *            An absolute path to a method library
	 * @return <code>true</code> if the method library needs to be upgraded
	 */
	public static boolean isMethodLibraryUpgradeRequired(String path) {
		if (true) {
			return org.eclipse.epf.persistence.migration.MappingUtil.conversionRequired(
					path + File.separator + XMILibraryManager.LIBRARY_XMI);
		}
		return PersistenceUtil.conversionRequired(path + File.separator
				+ XMILibraryManager.LIBRARY_XMI);
	}
	
	/**
	 * Creates a new XMI method library.
	 * 
	 * @param name
	 *            a name for the new method library
	 * @param path
	 *            an absolute path to a method library
	 * @return a method library
	 * @throw <code>LibraryServiceException</code> if an error occurs while
	 *        performing the operation
	 */
	public static MethodLibrary createMethodLibrary(String name, String path)
			throws LibraryServiceException {
		Map args = new HashMap();
		args.put(XMILibraryManager.ARG_LIBRARY_PATH, path);
		return LibraryService.getInstance().createMethodLibrary(name,
				XMILibraryManager.LIBRARY_TYPE, args);
	}

	/**
	 * Opens an existing method library.
	 * 
	 * @param path
	 *            an absolute path to a method library
	 * @return a method library
	 * @throw <code>LibraryServiceException</code> if an error occurs while
	 *        performing the operation
	 */
	public static MethodLibrary openMethodLibrary(String path)
			throws LibraryServiceException {
		Map args = new HashMap();
		args.put(XMILibraryManager.ARG_LIBRARY_PATH, path);
		return LibraryService.getInstance().openMethodLibrary(
				XMILibraryManager.LIBRARY_TYPE, args);
	}

	/**
	 * Opens the method library project associated with a method library.
	 * 
	 * @param library
	 *            a method library
	 * @param monitor
	 *            a progress monitor
	 */
	public static void openMethodLibraryProject(MethodLibrary library,
			IProgressMonitor monitor) {
		if (library != null) {
			IProject project = ResourceUtil.findProject(library);
			if (project != null && !project.isOpen()) {
				try {
					project.open(IResource.BACKGROUND_REFRESH, monitor);
				} catch (CoreException e) {
					XMILibraryPlugin.getDefault().getLogger().logError(e);
				}
			}
		}
	}

}
