package tools.vitruv.dsls.mirbase.validation;

import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.Path;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.osgi.service.resolver.VersionRange;
import org.eclipse.pde.core.project.IBundleProjectDescription;
import org.eclipse.pde.core.project.IBundleProjectService;
import org.eclipse.pde.core.project.IRequiredBundleDescription;
import org.eclipse.pde.internal.core.PDECore;
import org.eclipse.pde.internal.core.WorkspaceModelManager;
import java.lang.reflect.*;

// TODO DW: move this functionality to another plugin, if necessary. remove dependencies from this project
@SuppressWarnings("restriction")
public class EclipsePluginHelper {
	public static IProject getProject(Resource resource) {
		if ((resource == null) || (resource.getURI() == null)) {
			return null;
		}
		
		String resourcePlatformString = resource.getURI().toPlatformString(true);
		return ResourcesPlugin.getWorkspace().getRoot().getFile(new Path(resourcePlatformString)).getProject();
	}
	
	public static boolean isPluginProject(IProject project) {
		return WorkspaceModelManager.isPluginProject(project);
	}

	public static IBundleProjectService getBundleProjectService() {
		// The acquireService method signature was changes in 2018 from expecting the class name as string
		// to expecting the class itself. To avoid version incompatibilities, we employ reflection to
		// invoke the correct method version, depending on which is available.
		try {
			Method acquireMethod = PDECore.class.getMethod("acquireService");
			AnnotatedType[] parameters = acquireMethod.getAnnotatedParameterTypes();
			AnnotatedType firstParameter = parameters[0];
			Object argument;
			if (firstParameter.getType().getTypeName().contains("String")) {
				argument = IBundleProjectService.class.getName();
			} else {
				argument = IBundleProjectService.class;
			}
			return (IBundleProjectService) acquireMethod.invoke(PDECore.getDefault(), argument);
		} catch (NoSuchMethodException e) {
			e.printStackTrace();
			throw new IllegalStateException("Method acquireService was not found on PDECore");
		} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException | SecurityException e) {
			e.printStackTrace();
			throw new IllegalStateException("Method acquireService could not be called on PDECore");
		}
		
	}
	
	public static IBundleProjectDescription getDescription(IProject project) throws CoreException {
		return getBundleProjectService().getDescription(project);
	}
	
	/**
	 * Does not check version.
	 * @param project
	 * @param bundleName
	 * @return
	 * @throws CoreException 
	 */
	public static boolean hasDependency(IProject project, String bundleName) throws CoreException {
		final IRequiredBundleDescription[] requiredBundles = getDescription(project).getRequiredBundles();
		if (requiredBundles == null)
			return false;
		
		for (IRequiredBundleDescription description : requiredBundles) {
			String[] bundleNameOptionsSplit = bundleName.split(";");
			if (bundleNameOptionsSplit.length < 1) {
				throw new IllegalStateException("There must be at least one segment in the name");
			}
			String bundleWithoutOptions = bundleNameOptionsSplit[0];
			if (description.getName().equals(bundleWithoutOptions)) {
				return true;
			}
		}
		
		return false;
	}
	
	public static void addDependency(IProject project, String bundleName) throws CoreException {
		final IBundleProjectDescription description = getDescription(project);
		if (hasDependency(project, bundleName)) {
			throw new IllegalArgumentException("Project " + project.getName() + " already has dependency to " + bundleName);
		}
		IRequiredBundleDescription[] requiredBundles = description.getRequiredBundles();
		IRequiredBundleDescription[] newBundles;
		if (requiredBundles != null) {
			newBundles = new IRequiredBundleDescription[requiredBundles.length + 1];
			System.arraycopy(requiredBundles, 0, newBundles, 0, requiredBundles.length);
		} else {
			newBundles = new IRequiredBundleDescription[1];
		}
		newBundles[newBundles.length - 1] = getBundleProjectService().newRequiredBundle(bundleName, null, false, false);
		description.setRequiredBundles(newBundles);
		description.apply(null);
	}
	
	public static IRequiredBundleDescription newRequiredBundle(IBundleProjectService service, String name, VersionRange range, boolean optional, boolean export) {
		return service.newRequiredBundle(name, range, optional, export);
	}
}

@@ -619,34 +619,103 @@
 er;%0A
-import java.lang.reflect.*
+%0Aimport com.google.common.collect.Lists;%0A%0Aimport java.lang.reflect.*;%0Aimport java.util.Optional
 ;%0A%0A/
@@ -1683,71 +1683,370 @@
 %0A%09%09%09
-Method acquireMethod = PDECore.class.getMethod(%22acquireService%22
+Optional%3CMethod%3E potentialAcquireMethod = Lists.newArrayList(PDECore.class.getMethods()).stream().filter(method -%3E method.getName().equals(%22acquireService%22)).findFirst();%0A%09%09%09if (!potentialAcquireMethod.isPresent()) %7B%0A%09%09%09%09throw new IllegalStateException(%22Method acquireService was not found on PDECore%22);%0A%09%09%09%7D%0A%09%09%09Method acquireMethod = potentialAcquireMethod.get(
 );%0A%09
@@ -2464,156 +2464,8 @@
 t);%0A
-%09%09%7D catch (NoSuchMethodException e) %7B%0A%09%09%09e.printStackTrace();%0A%09%09%09throw new IllegalStateException(%22Method acquireService was not found on PDECore%22);%0A
 %09%09%7D 

Fix a bug in the retrieval of acquireService method