/*
 * (c) Copyright Martin Kahr, Sysdeo SA 2001, 2002.
 * All Rights Reserved.
 */

package com.sysdeo.eclipse.tomcat;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

import org.eclipse.core.runtime.IPath;
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.jdt.internal.ui.wizards.dialogfields.CheckedListDialogField;
import org.eclipse.jdt.internal.ui.wizards.dialogfields.LayoutUtil;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;

/**
 * This class adds a selection list to the tomcat plugin project configuration.
 *
 * the selection is read and written to the file ".webclasspath" in the
 * root of the project.
 *
 * A special WebAppClassLoader implementation for tomcat 4.x loads the
 * generated file during startup of the webapplication.
 *
 * @version 	1.0
 * @author		Martin Kahr (martin.kahr@brainment.com)
 */
public class TomcatProjectWebclasspathPropertyPage {

	/** CheckedListDialogField */
	private CheckedListDialogField<String> cpList = null;
	/** Button */
	private Button webClassPathCheck = null;
	/** WebClassPathEntries */
	private WebClassPathEntries entries;
	/**  */
	private ArrayList<String> visitedProjects = new ArrayList<>();

	/** IJavaProject */
	private final IJavaProject javaProject;
	/** TomcatProject */
	private final TomcatProject tomcatProject;

	/**
	 * Constructor
	 * @param java IJavaProject
	 * @param tomcat TomcatProject
	 */
	public TomcatProjectWebclasspathPropertyPage(final IJavaProject java, final TomcatProject tomcat) {
		this.javaProject = java;
		this.tomcatProject = tomcat;
	}

	/**
	 *
	 * @return IJavaProject
	 */
	public IJavaProject getJavaProject() {
		return this.javaProject;
	}

	/**
	 * okay has been pressed
	 * @return boolean
	 */
	public boolean performOk() {
		java.util.List<String> newSelection = this.cpList.getCheckedElements();

		if (this.webClassPathCheck.getSelection()) {
			this.tomcatProject.setWebClassPathEntries(new WebClassPathEntries(newSelection));
		} else {
			this.tomcatProject.setWebClassPathEntries(null);
		}
		this.tomcatProject.saveProperties();
		return true;
	}

	/**
	 *
	 * @param ctrl Composite
	 * @return Control
	 */
	public Control getControl(final Composite ctrl) {
		Composite group = new Composite(ctrl, SWT.NONE);
		GridLayout layout = new GridLayout();
		layout.numColumns = 2;
		group.setLayout(layout);

		this.webClassPathCheck = new Button(group, SWT.CHECK | SWT.LEFT);
		this.webClassPathCheck.setText(TomcatPluginResources.PROPERTIES_PAGE_PROJECT_ACTIVATE_DEVLOADER_LABEL);
		this.webClassPathCheck.setEnabled(true);
		this.webClassPathCheck.setSelection(isActive());
		Adapter listner = new Adapter(this.cpList, this.webClassPathCheck);
		this.webClassPathCheck.addSelectionListener(listner);
		this.entries = listner.getEntries();

		this.cpList = new CheckedListDialogField<>(
				null, new String[]{"Check All", "Uncheck All"}, new LabelProvider());
		this.cpList.setEnabled(isActive());
		this.cpList.setCheckAllButtonIndex(0);
		this.cpList.setUncheckAllButtonIndex(1);

		ArrayList<String> classPathEntries = new ArrayList<>();
		getClassPathEntries(getJavaProject(), classPathEntries);

		if (this.entries != null) {
			List<String> selected = this.entries.getList();
			// check for entries which are still in the list but no more in classpath entries list and remove them
			for (Iterator<String> it = selected.iterator(); it.hasNext();) {
				String sel = it.next();
				if (!classPathEntries.contains(sel)) {
					it.remove();
				}
			}
		}

		// sort the entries
		Collections.sort(classPathEntries);

		this.cpList.setElements(classPathEntries);
		if (this.entries != null) {
			this.cpList.setCheckedElements(this.entries.getList());
		}

		this.cpList.doFillIntoGrid(group, 3);
		LayoutUtil.setHorizontalGrabbing(this.cpList.getListControl(null));
		return group;
	}

	/**
	 *
	 * @param prj IJavaProject
	 * @param data ArrayList<String>
	 */
	public void getClassPathEntries(final IJavaProject prj, final ArrayList<String> data) {
		IClasspathEntry[] cpEntries = null;
		IPath outputPath = null;
		try {
			outputPath = prj.getOutputLocation();
			add(data, prj.getOutputLocation());
			cpEntries = prj.getRawClasspath();
			// cpEntries = prj.getResolvedClasspath(false);
		} catch (final JavaModelException e) {
			TomcatLauncherPlugin.log(e);
		}
		if (cpEntries != null) {
			getClassPathEntries(cpEntries, prj, data, outputPath);
		}
	}

	/**
	 *
	 * @param cpEntries IClasspathEntry[]
	 * @param prj IJavaProject
	 * @param data ArrayList<String>
	 * @param outputPath IPath
	 */
	private void getClassPathEntries(final IClasspathEntry[] cpEntries, final IJavaProject prj,
					final ArrayList<String> data, final IPath outputPath) {
		for (final IClasspathEntry entry : cpEntries) {
			if (entry.getEntryKind() == IClasspathEntry.CPE_PROJECT) {
				String prjName = entry.getPath().lastSegment();
				if (!this.visitedProjects.contains(prjName)) {
					this.visitedProjects.add(prjName);
					getClassPathEntries(getJavaProject().getJavaModel().getJavaProject(prjName), data);
				}
			} else if (entry.getEntryKind() == IClasspathEntry.CPE_LIBRARY) {
				add(data, entry.getPath());
			} else if (entry.getEntryKind() == IClasspathEntry.CPE_SOURCE) {
				IPath path = entry.getOutputLocation();
				if (path != null && !path.equals(outputPath)) {
					add(data, path);
				}
			} else if (entry.getEntryKind() == IClasspathEntry.CPE_CONTAINER) {
				if (!"org.eclipse.jdt.launching.JRE_CONTAINER".equals(entry.getPath().toString())) {
					// Add container itself, as TomcatBootstrap can actually process them
					// at the moment
					// Basically, users will be able to choose b/w the whole container
					// or some artifacts enclosed by it
					add(data, entry.getPath());

					// Expand container and add its content as individual
					// elements
					IClasspathContainer container;
					try {
						container =
						JavaCore.getClasspathContainer(entry.getPath(), prj);
					} catch (final JavaModelException e) {
						TomcatLauncherPlugin.log("failed to obtain classpath container '"
										+ entry.getPath() + "'"
										+ " for project '" + prj.getProject().getName() + "'");
						TomcatLauncherPlugin.log(e);
						container = null;
					}

					if (container != null) {
						getClassPathEntries(container.getClasspathEntries(), prj, data, outputPath);
					}
				}
			} else {
				add(data, entry.getPath());
			}
		}
	}

	/**
	 *
	 * @param data ArrayList<String>
	 * @param entry IPath
	 */
	private void add(final ArrayList<String> data, final IPath entry) {
		String path = entry.toFile().toString().replace('\\', '/');
		// ignore tomcat's own libs and the JRE paths..
		if (!data.contains(path) && path.indexOf("TOMCAT_HOME") == -1
						&& path.indexOf("JRE_CONTAINER") == -1 && path.indexOf("JRE_LIB") == -1) {
			data.add(path.replace('\\', '/'));
		}
	}

	/**
	 *
	 * @return boolean
	 */
	private boolean isActive() {
		this.entries = null;
		TomcatProject project = this.tomcatProject;
		if (project == null) {
			return false;
		}
		this.entries = project.getWebClassPathEntries();
		return this.entries != null;
	}

	/**
	 * Adapter
	 *
	 */
	static final class Adapter extends SelectionAdapter {

		/** CheckedListDialogField */
		private final CheckedListDialogField<String> cpList;
		/** Button */
		private final Button webClassPathCheck;
		/** WebClassPathEntries */
		private WebClassPathEntries entries;

		/**
		 * Constructor
		 * @param list CheckedListDialogField
		 * @param button Button
		 */
		Adapter(final CheckedListDialogField<String> list, final Button button) {
			this.cpList = list;
			this.webClassPathCheck = button;
		}

		/**
		 *
		 * @return WebClassPathEntries
		 */
		public WebClassPathEntries getEntries() {
			return this.entries;
		}

		/**
		 * @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent)
		 */
		@Override
		public void widgetSelected(final SelectionEvent ev) {
			if (this.webClassPathCheck.getSelection()) {
				this.entries = new WebClassPathEntries();
				this.cpList.setEnabled(true);
			} else {
				this.entries = null;
				this.cpList.setEnabled(false);
			}
		}
	}
}
