/*******************************************************************************
 * Copyright (c) 2003, 2007 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 API and implementation
 *******************************************************************************/
package org.eclipse.jst.j2ee.internal.web.operations;

import java.util.Enumeration;
import java.util.Hashtable;
import java.util.List;
import java.util.Vector;

import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jem.util.logger.proxy.Logger;
import org.eclipse.jem.workbench.utility.JemProjectUtilities;
import org.eclipse.jst.j2ee.internal.J2EEConstants;
import org.eclipse.jst.j2ee.internal.project.ProjectSupportResourceHandler;
import org.eclipse.jst.j2ee.web.componentcore.util.WebArtifactEdit;
import org.eclipse.wst.common.componentcore.ComponentCore;
import org.eclipse.wst.common.componentcore.resources.IVirtualComponent;
import org.eclipse.wst.common.componentcore.resources.IVirtualFolder;

import com.ibm.icu.util.StringTokenizer;

public class WebPropertiesUtil {
	// private static final char[] BAD_CHARS = {'/', '\\', ':'};
	private static final char[] BAD_CHARS = {':'};
	public static final String DEFAULT_JAVA_SOURCE_NAME = "Java Source"; //$NON-NLS-1$
	//TODO Port to flexible structure
	/**
	 * Update the Web Content folder to a new value if it is different. This applies to both Static
	 * and J2EE Web Projects. In the case of a J2EE Project, the library classpath entries will be
	 * modifies to reflect the new location.
	 * 
	 * @param project
	 *            The Web Project to update
	 * @param webContentName
	 *            The new name given to the Web Project's Web Content folder
	 * @param progressMonitor
	 *            Indicates progress of the update operation
	 * @return True if the web content rename was actually renamed, false if unneeded.
	 * @throws CoreException
	 *             The exception that occured during renaming of the the project's web content
	 *             folder
	 */
	public static boolean updateWebContentNameAndProperties(IProject project, String webContentName, IProgressMonitor progressMonitor) throws CoreException {
		boolean success = false;
		if (project.exists() && project.isOpen()) {

			/*
			 * IBaseWebNature webNature = J2EEWebNatureRuntimeUtilities.getRuntime(project); if
			 * (webContentName == null) { if (webNature.isStatic()) { webContentName =
			 * J2EEWebNatureRuntimeUtilities.getDefaultStaticWebContentName(); } else {
			 * webContentName = J2EEWebNatureRuntimeUtilities.getDefaultJ2EEWebContentName(); } }
			 */

			IPath newPath = new Path(webContentName);
			if (getModuleServerRoot(project).getProjectRelativePath().equals(newPath))
				return false;
			if (project.exists(newPath)) {
				IStatus status = new Status(IStatus.ERROR, "org.eclipse.jst.j2ee", IStatus.OK, ProjectSupportResourceHandler.getString(ProjectSupportResourceHandler.Could_not_rename_____2, new Object[]{webContentName}), null); //$NON-NLS-1$ //$NON-NLS-2$	
				throw new CoreException(status);
			}

			moveWebContentFolder(project, webContentName, progressMonitor);
			updateWebContentNamePropertiesOnly(project, webContentName, progressMonitor);
			success = true;
		}
		return success;
	}

	/**
	 * Update the classpath entries and Server Root Name for this web project only.
	 * 
	 * @param project
	 * @param webContentName
	 * @return
	 */
	public static void updateWebContentNamePropertiesOnly(IProject project, String webContentName, IProgressMonitor progressMonitor) throws CoreException {
		IPath newPath = new Path(webContentName);
		if (getModuleServerRoot(project).equals(newPath))
			return;

		if (!getModuleServerRoot(project).equals(webContentName)) {

			// if (webModuleArtifact.isJ2EE) {
			// Update the library references
			IJavaProject javaProject = JemProjectUtilities.getJavaProject(project);

			IClasspathEntry[] classpath = javaProject.getRawClasspath();
			IClasspathEntry[] newClasspath = new IClasspathEntry[classpath.length];

			for (int i = 0; i < classpath.length; i++) {
				if (classpath[i].getEntryKind() == IClasspathEntry.CPE_LIBRARY) {
					IClasspathEntry library = classpath[i];
					IPath libpath = library.getPath();
					IPath modServerRootPath = getModuleServerRoot(project).getFullPath();
					if (modServerRootPath.isPrefixOf(libpath)) {
						IPath prunedPath = libpath.removeFirstSegments(modServerRootPath.segmentCount());
						IPath relWebContentPath = new Path(webContentName + "/" + prunedPath.toString()); //$NON-NLS-1$
						IResource absWebContentPath = project.getFile(relWebContentPath);

						IPath srcAttachmentPath = library.getSourceAttachmentPath();
						if (null != srcAttachmentPath) {
							prunedPath = srcAttachmentPath.removeFirstSegments(modServerRootPath.segmentCount());
						}
						IResource absWebContentSrcAttachmentPath = project.getFile(relWebContentPath);

						newClasspath[i] = JavaCore.newLibraryEntry(absWebContentPath.getFullPath(), absWebContentSrcAttachmentPath.getFullPath(), library.getSourceAttachmentRootPath(), library.isExported());

					} else {
						newClasspath[i] = classpath[i];
					}

				} else {
					newClasspath[i] = classpath[i];
				}
				// }

				// Set the java output folder
				IFolder outputFolder = project.getFolder(getModuleServerRoot(project).getFullPath());
				javaProject.setRawClasspath(newClasspath, outputFolder.getFullPath(), new SubProgressMonitor(progressMonitor, 1));
			}
			// update websettings
			// TODO add to WebArtifactEdit
			// webNature.setModuleServerRootName(webContentName);
		}
	}

	/**
	 * Moves the web content folder to the name indicated only if that path doesn't already exist in
	 * the project.
	 * 
	 * @param project
	 *            The web project to be updated.
	 * @param webContentName
	 *            The new web content name
	 * @param progressMonitor
	 *            Indicates progress
	 * @throws CoreException
	 *             The exception that occured during move operation
	 */
	public static void moveWebContentFolder(IProject project, String webContentName, IProgressMonitor progressMonitor) throws CoreException {
		IPath newPath = new Path(webContentName);
		if (!project.exists(newPath)) {
			if (newPath.segmentCount() > 1) {
				for (int i = newPath.segmentCount() - 1; i > 0; i--) {
					IPath tempPath = newPath.removeLastSegments(i);
					IFolder tempFolder = project.getFolder(tempPath);
					if (!tempFolder.exists()) {
						tempFolder.create(true, true, null);
					}
				}
			}
			newPath = project.getFullPath().append(newPath);
			IContainer webContentRoot = getModuleServerRoot(project);
			IPath oldPath = webContentRoot.getProjectRelativePath();
			webContentRoot.move(newPath, IResource.FORCE | IResource.KEEP_HISTORY, new SubProgressMonitor(progressMonitor, 1));
			for (int i = 0; i < oldPath.segmentCount(); i++) {
				IPath tempPath = oldPath.removeLastSegments(i);
				IFolder tempFolder = project.getFolder(tempPath);
				if (tempFolder.exists() && tempFolder.members().length == 0) {
					tempFolder.delete(true, true, null);
				}
			}
		}
	}

	/**
	 * Synchonizies the class path and the lib directories to catch any changes from the last use
	 * Creation date: (4/17/01 11:48:12 AM)
	 */
	protected static void synch(IProject project, IProgressMonitor monitor) {

		try {
			if (monitor == null) {
				monitor = new NullProgressMonitor();
			}
			monitor.beginTask(ProjectSupportResourceHandler.Sychronize_Class_Path_UI_, 4); //$NON-NLS-1$
			//$NON-NLS-1$ = "Sychronize Class Path"

			IContainer lib_folder = getWebLibFolder(project);
			// Nothing to do if the lib folder does not exist.
			if (lib_folder == null || !lib_folder.isAccessible())
				return;
			IJavaProject javaProject = JemProjectUtilities.getJavaProject(project);
			IPath lib_path = lib_folder.getProjectRelativePath();
			IPath lib_full_path = lib_folder.getFullPath();

			IClasspathEntry[] cp = javaProject.getRawClasspath();

			boolean needsToBeModified = false;
			// Create a map of the lib projects in the current project
			Hashtable lib_jars = new Hashtable();
			IResource[] children = lib_folder.members();
			monitor.subTask(ProjectSupportResourceHandler.Catalog_Lib_Directory__UI_); //$NON-NLS-1$
			//$NON-NLS-1$ = "Catalog Lib Directory:"
			for (int j = 0; j < children.length; j++) {
				IResource child = children[j];
				// monitor.setTaskName(ResourceHandler.getString("Catalog_Lib_Directory__UI_") +
				// child); //$NON-NLS-1$ = "Catalog Lib Directory:"
				// Make sure it is a zip or a jar file
				if (child.getType() == IResource.FILE && (child.getFullPath().toString().toLowerCase().endsWith(".jar") //$NON-NLS-1$
							|| child.getFullPath().toString().toLowerCase().endsWith(".zip"))) { //$NON-NLS-1$
					lib_jars.put(child.getFullPath(), child);
				}

			}

			monitor.worked(1);
			monitor.subTask(ProjectSupportResourceHandler.Update_ClassPath__UI_); //$NON-NLS-1$
			//$NON-NLS-1$ = "Update ClassPath:"
			// Loop through all the classpath dirs looking for ones that may have
			// been deleted
			Vector newClassPathVector = new Vector();
			for (int j = 0; j < cp.length; j++) {

				// If it is a lib_path
				if (cp[j].getPath().toString().startsWith(lib_path.toString()) || cp[j].getPath().toString().startsWith(lib_full_path.toString())) {
					// It was already in the class path
					if (lib_jars.get(cp[j].getPath()) != null) {
						newClassPathVector.add(cp[j]);
						// Remove it from the hash table of paths to add back
						// monitor.setTaskName(ResourceHandler.getString("Catalog_Lib_Directory__UI_")
						// + cp[j].getPath()); //$NON-NLS-1$ = "Catalog Lib Directory:"
						lib_jars.remove(cp[j].getPath());

					} else {
						// You have removed something form the class path you
						// will need to re-build
						// monitor.setTaskName(ResourceHandler.getString("Catalog_Lib_Directory_Remo_UI_")
						// + cp[j].getPath()); //$NON-NLS-1$ = "Catalog Lib Directory:Remove "
						needsToBeModified = true;
					}
				} else {
					monitor.subTask(ProjectSupportResourceHandler.Catalog_Lib_Directory__UI_ + cp[j].getPath()); //$NON-NLS-1$
					//$NON-NLS-1$ = "Catalog Lib Directory:"
					newClassPathVector.add(cp[j]);
				}
			}
			monitor.worked(1);
			monitor.subTask(ProjectSupportResourceHandler.Update_ClassPath__UI_); //$NON-NLS-1$
			//$NON-NLS-1$ = "Update ClassPath:"

			// Add any entries not already found
			Enumeration aenum = lib_jars.keys();
			while (aenum.hasMoreElements()) {
				IPath path = (IPath) aenum.nextElement();
				newClassPathVector.add(JavaCore.newLibraryEntry(path, null, null));
				// You have added something form the class path you
				// will need to re-build
				// monitor.setTaskName(ResourceHandler.getString("23concat_UI_", (new Object[] {
				// path }))); //$NON-NLS-1$ = "Catalog Lib Directory:Add {0}"
				needsToBeModified = true;
			}

			monitor.worked(1);
			monitor.subTask(ProjectSupportResourceHandler.Set_ClassPath__UI_); //$NON-NLS-1$
			//$NON-NLS-1$ = "Set ClassPath:"

			// Tansfer the vector to an array
			IClasspathEntry[] newClassPathArray = new IClasspathEntry[newClassPathVector.size()];

			for (int j = 0; j < newClassPathArray.length; j++) {
				newClassPathArray[j] = (IClasspathEntry) newClassPathVector.get(j);
			}

			// Only change the class path if there has been a modification
			if (needsToBeModified) {

				try {
					javaProject.setRawClasspath(newClassPathArray, monitor);
				} catch (Exception e) {
					Logger.getLogger().log(e);
				}
			}

		} catch (ClassCastException ex) {
			Logger.getLogger().log(ex);
		} catch (CoreException ex) {
			Logger.getLogger().log(ex);
		} finally {
			monitor.done();
		}
	}

	public static void updateContextRoot(IProject project, String contextRoot) {
		if (project.exists() && project.isOpen()) {
			WebArtifactEdit webEdit = null;
			try {
				// TODO migrate to flex projects
				// webEdit = (WebArtifactEdit) StructureEdit.getFirstArtifactEditForRead(project);
				if (webEdit != null)
					webEdit.setServerContextRoot(contextRoot);
			} finally {
				if (webEdit != null)
					webEdit.dispose();
			}

		}
	}


	/**
	 * @param project
	 *            org.eclipse.core.resources.IProject
	 */
	/**
	 * Returns a error message that states whether a context root is valid or not returns null if
	 * context root is fine
	 * 
	 * @return java.lang.String
	 * @param contextRoot
	 *            java.lang.String
	 */
	public static String validateContextRoot(String contextRoot) {

		if (contextRoot == null)
			return null;

		String errorMessage = null;

		String name = contextRoot;
		if (name.equals("") || name == null) { //$NON-NLS-1$
			// this was added because the error message shouldnt be shown initially. It should be
			// shown only if context root field is edited to
			errorMessage = ProjectSupportResourceHandler.Context_Root_cannot_be_empty_2; //$NON-NLS-1$
			return errorMessage;
		}

		/*******************************************************************************************
		 * // JZ - fix to defect 204264, "/" is valid in context root if (name.indexOf("//") != -1) {
		 * //$NON-NLS-1$ errorMessage = "// are invalid characters in a resource name"; return
		 * errorMessage; }
		 ******************************************************************************************/

		if (name.trim().equals(name)) {
			StringTokenizer stok = new StringTokenizer(name, "."); //$NON-NLS-1$
			outer : while (stok.hasMoreTokens()) {
				String token = stok.nextToken();
				for (int i = 0; i < token.length(); i++) {
					if (!(token.charAt(i) == '_') && !(token.charAt(i) == '-') && !(token.charAt(i) == '/') && Character.isLetterOrDigit(token.charAt(i)) == false) {
						if (Character.isWhitespace(token.charAt(i))) {
							// Removed because context roots can contain white space
							// errorMessage =
							// ResourceHandler.getString("_Context_root_cannot_conta_UI_");//$NON-NLS-1$
							// = " Context root cannot contain whitespaces."
						} else {
							errorMessage = ProjectSupportResourceHandler.getString(ProjectSupportResourceHandler.The_character_is_invalid_in_a_context_root, new Object[]{(new Character(token.charAt(i))).toString()}); //$NON-NLS-1$
							break outer;
						}
					}
				}
			}
		} // en/ end of if(name.trim
		else
			errorMessage = ProjectSupportResourceHandler.Names_cannot_begin_or_end_with_whitespace_5; //$NON-NLS-1$

		return errorMessage;
	}


	/**
	 * Return true if the string contains any of the characters in the array.
	 */
	private static boolean contains(String str, char[] chars) {
		for (int i = 0; i < chars.length; i++) {
			if (str.indexOf(chars[i]) != -1)
				return true;
		}
		return false;
	}


	public static String validateFolderName(String folderName) {
		if (folderName.length() == 0)
			return ProjectSupportResourceHandler.Folder_name_cannot_be_empty_2; //$NON-NLS-1$

		if (contains(folderName, BAD_CHARS))
			return ProjectSupportResourceHandler.getString(ProjectSupportResourceHandler.Folder_name_is_not_valid, new Object[]{folderName}); //$NON-NLS-1$

		return null;
	}


	public static String validateWebContentName(String webContentName, IProject project, String javaSourceName) {

		String msg = validateFolderName(webContentName);
		if (msg != null)
			return msg;

		if (javaSourceName != null && webContentName.equals(javaSourceName))
			return ProjectSupportResourceHandler.Folder_names_cannot_be_equal_4; //$NON-NLS-1$

		// If given a java project, check to make sure current package fragment
		// root folders do not overlap with new web content name
		if (project != null) {
			IJavaProject javaProject = JemProjectUtilities.getJavaProject(project);
			if (javaProject != null) {
				try {
					IPackageFragmentRoot roots[] = javaProject.getPackageFragmentRoots();
					for (int i = 0; i < roots.length; i++) {
						IPackageFragmentRoot root = roots[i];
						if (!root.isArchive()) {
							IResource resource = root.getCorrespondingResource();
							if (resource.getType() == IResource.FOLDER) {
								IPath path = resource.getFullPath();
								String rootFolder = path.segment(1);
								if (webContentName.equals(rootFolder)) {
									if (root.getKind() == IPackageFragmentRoot.K_SOURCE)
										return ProjectSupportResourceHandler.Folder_name_cannot_be_the_same_as_Java_source_folder_5; //$NON-NLS-1$

									return ProjectSupportResourceHandler.Folder_name_cannot_be_the_same_as_Java_class_folder_6; //$NON-NLS-1$
								}
							}
						}
					}
				} catch (JavaModelException e) {
					return null;
				}
			}
		}

		return null;
	}


	/**
	 * Update given web nature to the current version if necessary.
	 * 
	 * @param webNature
	 *            The web Nature that should be examined.
	 * @return True if successful, false if unnecessary.
	 * @throws CoreException
	 *             The exception that occured during the version change operation.
	 */
	/*
	 * static public boolean updateNatureToCurrentVersion(J2EEWebNatureRuntime webNature) throws
	 * CoreException {
	 * 
	 * boolean success = false;
	 * 
	 * if (webNature.getVersion() != WEB.CURRENT_VERSION) {
	 * webNature.setVersion(J2EESettings.CURRENT_VERSION); success = true; }
	 * ((J2EEModuleWorkbenchURIConverterImpl)
	 * webNature.getResourceSet().getURIConverter()).recomputeContainersIfNecessary();
	 * 
	 * return success; }
	 */

	/**
	 * Move the old source folder to the new default folder.
	 * 
	 * @param project
	 *            The Web Project we are working with.
	 * @param oldSourceFolder
	 *            The old "Java Source" folder that will be moved.
	 * @param javaSourceName
	 *            The new name of the "Java Source" folder, or null for default.
	 * @return The location of the new folder, or null if no move was necessary.
	 * @throws CoreException
	 *             The exception that occured during the move operation.
	 */
	static public IContainer updateJavaSourceName(IProject project, IContainer oldSourceFolder, String javaSourceName, IProgressMonitor progressMonitor) throws CoreException {
		IContainer newSourceFolder = null;
		if (oldSourceFolder != null) {
			IPath newPath;
			if (javaSourceName == null)
				newPath = new Path(DEFAULT_JAVA_SOURCE_NAME);
			else
				newPath = new Path(javaSourceName);

			// Make sure new path is different form old path
			if (!project.getFolder(newPath).getFullPath().equals(oldSourceFolder.getFullPath())) {
				oldSourceFolder.move(newPath, IResource.FORCE | IResource.KEEP_HISTORY, new SubProgressMonitor(progressMonitor, 1));
				JemProjectUtilities.removeFromJavaClassPath(project, oldSourceFolder);
				newSourceFolder = project.getFolder(newPath);
				JemProjectUtilities.appendJavaClassPath(project, JavaCore.newSourceEntry(project.getFolder(newPath).getFullPath()));
			}
		}
		return newSourceFolder;
	}


	/**
	 * Get the source folder that should be used for migration.
	 * 
	 * @param project
	 *            The Web Project to examine.
	 * @return The source folder to use in migration, or null if it should be skipped.
	 */
	static public IContainer getJavaSourceFolder(IProject project) {
		List sourceRoots = JemProjectUtilities.getSourceContainers(project);
		IContainer oldSourceFolder = null;

		if (sourceRoots != null) {
			if (sourceRoots.size() == 1) {
				IContainer sourceFolder = (IContainer) sourceRoots.get(0);
				if (sourceFolder instanceof IFolder) {
					oldSourceFolder = sourceFolder;
				}
			}
		}
		return oldSourceFolder;
	}

	public static IFolder getModuleServerRoot(IProject project) {
		// TODO need to implement module server root properly
		IPath compRootPath = ComponentCore.createComponent(project).getRootFolder().getUnderlyingFolder().getProjectRelativePath();
		return project.getFolder(compRootPath);
		//return project.getFolder("WebContent");
		
	}

	public static IVirtualFolder getWebLibFolder(IVirtualComponent webComponent) {
		IPath path = new Path(J2EEConstants.WEB_INF + "/" + "lib");
		IVirtualFolder libFolder = webComponent.getRootFolder().getFolder(path);
		return libFolder;
	}

	//TODO delete jsholl
	/**
	 * @deprecated use getWebLibFolder(IVirtualComponent webComponent)
	 * @param project
	 * @return
	 */
	public static IContainer getWebLibFolder(IProject project) {
		return getWebLibFolder(ComponentCore.createComponent(project)).getUnderlyingFolder();
	}

	//	
	// static public boolean isImportedClassesJARFileInLibDir(IResource resource) {
	// if (resource == null || !resource.exists())
	// return false;
	// return resource.getType() == resource.FILE &&
	// resource.getName().endsWith(IWebNatureConstants.IMPORTED_CLASSES_SUFFIX) && isZip(resource);
	// }
	//	
	// static public boolean isLibDirJARFile(IResource resource) {
	// if (resource == null || !resource.exists())
	// return false;
	// return resource.getType() == resource.FILE && isZip(resource);
	// }
	//	
	// static public boolean isZip(IResource resource) {
	// String path = resource.getLocation().toOSString();
	// ZipFile zip = null;
	//
	// try {
	// zip = new ZipFile(path);
	// } catch (IOException notAZip) {
	// return false;
	// } finally {
	// if (zip != null) {
	// try {
	// zip.close();
	// } catch (IOException ex) {}
	// }
	// }
	// return zip != null;
	// }


}
