package org.eclipse.stp.sc.common.utils;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.StringTokenizer;
import java.util.jar.Attributes;
import java.util.jar.JarFile;
import java.util.jar.Manifest;

import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.jdt.core.ClasspathContainerInitializer;
import org.eclipse.jdt.core.IClasspathContainer;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.stp.common.logging.LoggingProxy;
import org.eclipse.stp.sc.common.runtime.RuntimeClasspathContainerInitializer;
import org.eclipse.stp.sc.common.runtime.UpdatedClasspathContainer;
import org.eclipse.wst.server.core.IRuntime;
import org.eclipse.wst.server.core.ServerCore;

public class RuntimeUtils {
	
	private static final LoggingProxy LOG = LoggingProxy.getlogger(RuntimeUtils.class);

	private static IJavaProject fDummyProject = createPlaceholderProject();
	
	/**
	 * get list of jar name defined in MANIFEST.MF/classpath for input jar file   
	 * @param jarFileName, jar file name
	 * @return, jar names defined in MANIFEST.ME/classpath
	 */
	public static String[] getLibEntryInManifest(String jarFileName) {
		File facadeFile = new File(jarFileName);
		ArrayList<String> libs = new ArrayList<String>();
		try {
			JarFile facadeJar = new JarFile(facadeFile);
			Manifest mfst = facadeJar.getManifest();
			Attributes attrs = mfst.getMainAttributes();
			String path = attrs.getValue("Class-Path");
			StringTokenizer tkzr = new StringTokenizer(path);
			String libEntry = null;
			while (tkzr.hasMoreTokens()) {
				libEntry = tkzr.nextToken();
				libs.add(libEntry);
			}
		} catch (IOException e) {
			LOG.debug("Exception getting jar facade", e);
		}
		return libs.toArray(new String[0]);
	}
	
	/**
	 * get all jars under one dir
	 * @param path, the path to look
	 * @return, the list of jars
	 */
	public static String[] getLibEntryByDir(String path) {
		File dir = new File(path);
		ArrayList<String> libs = new ArrayList<String>();
		if (!dir.exists() || !dir.isDirectory()) {
			return new String[0];
		}
		for (String fileName : dir.list()) {
			if (fileName.endsWith(".jar")) {
				libs.add(fileName);
			}
		}
		return libs.toArray(new String[0]);
	}
	
	public static IClasspathEntry getRuntimeLib(IRuntime runtime){
		if(runtime == null){
			return null;
		}
		
		String runtimeLibName = getRuntimeLibName(runtime);
		IClasspathEntry cpEntry =
			JavaCore.newContainerEntry(new Path(RuntimeClasspathContainerInitializer.ID).append(runtimeLibName), true);
		return cpEntry;
	}
	
	public static IClasspathEntry createRuntimeLib(IRuntime runtime){
		if(runtime == null){
			return null;
		}
		IClasspathEntry cpEntry = null;
		final String runtimeLibName = getRuntimeLibName(runtime);
		final IPath path = new Path(RuntimeClasspathContainerInitializer.ID).append(runtimeLibName);
		ClasspathContainerInitializer initializer = JavaCore.getClasspathContainerInitializer(RuntimeClasspathContainerInitializer.ID);
		try{
			IClasspathContainer container = new UpdatedClasspathContainer(runtime);
			initializer.requestClasspathContainerUpdate(
					path, 
					fDummyProject, 
					container);
			//refresh container
			JavaCore.setClasspathContainer(path, new IJavaProject[]{fDummyProject}, new IClasspathContainer[]{container}, null); 
			
			//update all the projects in workspace
			rebindClasspathEntries(path, container, null);
		}catch(CoreException ex){
			LOG.error("error while creating the runtime library " + runtimeLibName + "in User Libraries", ex);
		}
		return cpEntry;
	}
	
	public static void removeRuntimeLib(IRuntime runtime){
		if(runtime == null){
			return;
		}
		String runtimeLibName = getRuntimeLibName(runtime);
		try{
			IPath path = new Path(RuntimeClasspathContainerInitializer.ID).append(runtimeLibName);
			rebindClasspathEntries(path, null, null);
		}catch(CoreException ex){
			LOG.error("error while removing the runtime library " + runtimeLibName + "in User Libraries", ex);
		}
	}
	

	
	public static String getRuntimeLibName(IRuntime runtime){
		String retValue = null;
		if(runtime != null){
			retValue = runtime.getRuntimeType().getName() + " Library [" + runtime.getId() + "]";
		}
		return retValue;
	}
	
	public static IRuntime getRuntimeFromLibName(String name){
		IRuntime[] runtimes = ServerCore.getRuntimes();
		if(runtimes != null && runtimes.length > 0){
			for(IRuntime runtime : runtimes){
				String libName = getRuntimeLibName(runtime);
				if(libName != null && libName.equals(name)){
					return runtime;
				}
			}
		}
		return null;
	}
	
	private static IJavaProject createPlaceholderProject() {
		IJavaProject retValue = null;
		String name= "####internal"; //$NON-NLS-1$
		IWorkspaceRoot root= ResourcesPlugin.getWorkspace().getRoot();
		while (true) {
			IProject project= root.getProject(name);
			if (!project.exists()) {
				retValue = JavaCore.create(project);
				break;
			}
			name += '1';
		}
		return retValue;
	}
	
	public static void rebindClasspathEntries(IPath containerPath, IClasspathContainer container, IProgressMonitor monitor) throws JavaModelException {
		try {
			if (monitor != null) {
				monitor.beginTask("", 1); //$NON-NLS-1$
			}
			IWorkspaceRoot root= ResourcesPlugin.getWorkspace().getRoot();
			IJavaProject[] projects= JavaCore.create(root).getJavaProjects();
			
			ArrayList<IJavaProject> affectedProjects= new ArrayList<IJavaProject>();
			
			for (int i= 0; i < projects.length; i++) {
				IJavaProject project= projects[i];
				IClasspathEntry[] entries= project.getRawClasspath();
				for (int k= 0; k < entries.length; k++) {
					IClasspathEntry curr= entries[k];
					if (curr.getEntryKind() == IClasspathEntry.CPE_CONTAINER) {
						if (containerPath.equals(curr.getPath())) {
							affectedProjects.add(project);
							break;
						}				
					}
				}
			}
			if (!affectedProjects.isEmpty()) {
				IJavaProject[] affected= (IJavaProject[]) affectedProjects.toArray(new IJavaProject[affectedProjects.size()]);
				IClasspathContainer[] containers= new IClasspathContainer[affected.length];
				if(container != null){
					containers[0] = container;
				}
				JavaCore.setClasspathContainer(containerPath, affected, containers, monitor == null ? null : new SubProgressMonitor(monitor, 1));
			} 
		} finally {
			if (monitor != null) {
				monitor.done();
			}
		}
	}
}
