/**
 * Copyright (c) 2011 committers of YAKINDU 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:
 * 	committers of YAKINDU - initial API and implementation
 * 
 */
package org.yakindu.sct.commons;

import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.List;

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.Path;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.launching.JavaRuntime;

import com.google.common.collect.Lists;

/**
 * Factory for {@link ClassLoader}s based on {@link IProject}s inside the
 * workspace
 * 
 * @author koenemann - Initial contribution and API
 * @author holger willebrandt
 */
public class WorkspaceClassLoaderFactory {

	private final boolean resolveSimpleProjectReferences;

	public WorkspaceClassLoaderFactory() {
		this(false);
	}

	/**
	 * @param resolveSimpleProjectReferences
	 *            whether project references should be resolved for non-java
	 *            projects
	 */
	public WorkspaceClassLoaderFactory(boolean resolveSimpleProjectReferences) {
		this.resolveSimpleProjectReferences = resolveSimpleProjectReferences;
	}

	/**
	 * Creates a {@link ClassLoader} that can be used to load resources from the
	 * workspace.
	 * 
	 * @deprecated use factory method that takes a parent {@link ClassLoader}
	 */
	public ClassLoader createClassLoader(IProject project) {
		return createClassLoader(project, WorkspaceClassLoaderFactory.class.getClassLoader());
	}

	/**
	 * Creates a {@link ClassLoader} that can be used to load resources from the
	 * workspace.
	 */
	public ClassLoader createClassLoader(IProject project, ClassLoader parent) {
		final List<URL> urls = Lists.newArrayList();

		addClasspathEntries(project, urls);

		return URLClassLoader.newInstance(urls.toArray(new URL[urls.size()]), parent);
	}

	protected void addClasspathEntries(IProject project, List<URL> urls) {
		IJavaProject javaProject = toJavaProject(project);
		if (javaProject != null) {
			addJavaClasspathEntries(javaProject, urls);
		} else {
			try {
				urls.add(getFileSystemFile(project).toURI().toURL());
			} catch (MalformedURLException e1) {
				e1.printStackTrace();
			}
			if (resolveSimpleProjectReferences) {
				addReferencedProjectsClasspaths(project, urls);
			}
		}
	}

	protected void addReferencedProjectsClasspaths(IProject project,
			List<URL> urls) {
		try {
			IProject[] referencedProjects = project.getReferencedProjects();
			for (IProject iProject : referencedProjects) {
				addClasspathEntries(iProject, urls);
			}
		} catch (CoreException e) {
			e.printStackTrace();
		}
	}

	protected File getFileSystemFile(IResource resource) {
		return new File(resource.getWorkspace().getRoot().getRawLocation()
				.toFile().getAbsolutePath()
				+ File.separator
				+ resource.getFullPath().toFile().getAbsolutePath());
	}

	protected IJavaProject toJavaProject(IProject project) {
		IJavaProject javaProject = JavaCore.create(project);
		if (javaProject.exists()) {
			return javaProject;
		}
		return null;
	}

	protected void addJavaClasspathEntries(IJavaProject project, List<URL> urls) {
		try {
			urls.addAll(Lists.newArrayList(convertClassPath(JavaRuntime
					.computeDefaultRuntimeClassPath(project))));
		} catch (CoreException e) {
			e.printStackTrace();
		}
	}

	/**
	 * Converting string classpath to {@link URL}s.
	 */
	private static URL[] convertClassPath(String[] classPath) {
		final URL[] urls = new URL[classPath.length];
		for (int i = 0; i < classPath.length; i++) {
			final String entry = classPath[i];
			final IPath path = new Path(entry);
			try {
				final URL url = path.toFile().toURI().toURL();
				urls[i] = url;
			} catch (MalformedURLException e) {
				throw new RuntimeException(
						"Could not convert classpath entry to URL: "
								+ path.toString(), e);
			}
		}
		return urls;
	}

}
