package org.bndtools.builder;

import java.util.ArrayList;
import java.util.List;

import org.bndtools.api.BndtoolsConstants;
import org.eclipse.core.resources.ICommand;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IProjectDescription;
import org.eclipse.core.resources.IProjectNature;
import org.eclipse.core.resources.IWorkspaceRunnable;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;

public class BndProjectNature implements IProjectNature {

	private IProject project;

	@Override
	public IProject getProject() {
		return project;
	}

	@Override
	public void setProject(IProject project) {
		this.project = project;
	}

	@Override
	public void configure() throws CoreException {
		final IProjectDescription desc = project.getDescription();
		addBuilder(desc);
		updateProject(desc, true);
	}

	@Override
	public void deconfigure() throws CoreException {
		IProjectDescription desc = project.getDescription();
		removeBuilder(desc);
		updateProject(desc, false);
	}

	private static void addBuilder(IProjectDescription desc) {
		ICommand[] commands = desc.getBuildSpec();
		for (ICommand command : commands) {
			if (command.getBuilderName()
				.equals(BndtoolsConstants.BUILDER_ID))
				return;
		}

		ICommand[] nu = new ICommand[commands.length + 1];
		System.arraycopy(commands, 0, nu, 0, commands.length);

		ICommand command = desc.newCommand();
		command.setBuilderName(BndtoolsConstants.BUILDER_ID);
		nu[commands.length] = command;
		desc.setBuildSpec(nu);
	}

	private static void removeBuilder(IProjectDescription desc) {
		ICommand[] commands = desc.getBuildSpec();
		List<ICommand> nu = new ArrayList<>();
		for (ICommand command : commands) {
			if (!command.getBuilderName()
				.equals(BndtoolsConstants.BUILDER_ID)) {
				nu.add(command);
			}
		}
		desc.setBuildSpec(nu.toArray(new ICommand[0]));
	}

	private void installBndClasspath() throws CoreException {
		IJavaProject javaProject = JavaCore.create(project);
		IClasspathEntry[] classpath = javaProject.getRawClasspath();
		for (IClasspathEntry entry : classpath) {
			if (entry.getEntryKind() == IClasspathEntry.CPE_CONTAINER
				&& BndtoolsConstants.BND_CLASSPATH_ID.equals(entry.getPath()))
				return; // already installed
		}

		IClasspathEntry[] newEntries = new IClasspathEntry[classpath.length + 1];
		System.arraycopy(classpath, 0, newEntries, 0, classpath.length);
		newEntries[classpath.length] = JavaCore.newContainerEntry(BndtoolsConstants.BND_CLASSPATH_ID);

		javaProject.setRawClasspath(newEntries, null);
	}

	private void removeBndClasspath() throws CoreException {
		IJavaProject javaProject = JavaCore.create(project);
		IClasspathEntry[] classpath = javaProject.getRawClasspath();
		List<IClasspathEntry> newEntries = new ArrayList<>(classpath.length);

		boolean changed = false;
		for (IClasspathEntry entry : classpath) {
			if (entry.getEntryKind() == IClasspathEntry.CPE_CONTAINER
				&& BndtoolsConstants.BND_CLASSPATH_ID.equals(entry.getPath())) {
				changed = true;
			} else {
				newEntries.add(entry);
			}
		}

		if (changed)
			javaProject.setRawClasspath(newEntries.toArray(new IClasspathEntry[0]), null);
	}

	private void updateProject(final IProjectDescription desc, final boolean adding) throws CoreException {
		IWorkspaceRunnable runnable = monitor -> {
			project.setDescription(desc, monitor);
			if (adding) {
				installBndClasspath();
			} else {
				removeBndClasspath();
			}
		};
		project.getWorkspace()
			.run(runnable, null);
	}

}
