/*******************************************************************************
 * Copyright (c) 2000, 2018 IBM Corporation and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.jdt.internal.ui.wizards.buildpaths.newsourcepage;

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

import org.eclipse.swt.widgets.Shell;

import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;

import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;

import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.IWorkbenchPartReference;
import org.eclipse.ui.IWorkbenchSite;
import org.eclipse.ui.forms.widgets.FormText;
import org.eclipse.ui.part.ISetSelectionTarget;

import org.eclipse.jdt.internal.corext.buildpath.BuildpathDelta;
import org.eclipse.jdt.internal.corext.buildpath.IBuildpathModifierListener;

import org.eclipse.jdt.internal.ui.JavaPlugin;

public abstract class BuildpathModifierAction extends Action implements ISelectionChangedListener {

	public static final int ADD_SEL_SF_TO_BP= 0;
	public static final int REMOVE_FROM_BP= 1;
	public static final int EXCLUDE= 2;
	public static final int UNEXCLUDE= 3;
	public static final int EDIT_FILTERS= 4;
	public static final int CREATE_LINK= 5;
	public static final int RESET_ALL= 6;
	public static final int EDIT_OUTPUT= 7;
	public static final int CREATE_OUTPUT= 8;
	public static final int RESET= 9;
	public static final int INCLUDE= 10;
	public static final int UNINCLUDE= 11;
	public static final int CREATE_FOLDER= 12;
	public static final int ADD_JAR_TO_BP= 13;
	public static final int ADD_LIB_TO_BP= 14;
	public static final int ADD_SEL_LIB_TO_BP= 15;
	public static final int CONFIGURE_BUILD_PATH= 16;
	public static final int RESET_ALL_OUTPUT_FOLDERS= 17;
	public static final int DROP_DOWN_ACTION= 18;
	public static final int ADD_SEL_LIB_TO_TEST_BP= 19;

	private final IWorkbenchSite fSite;
	private final List<Object> fSelectedElements;
	private final ISetSelectionTarget fSelectionTarget;
	private final List<IBuildpathModifierListener> fListeners;

	public BuildpathModifierAction(IWorkbenchSite site, ISetSelectionTarget selectionTarget, int id) {
		this(site, selectionTarget, id, IAction.AS_PUSH_BUTTON);
    }

	public BuildpathModifierAction(IWorkbenchSite site, ISetSelectionTarget selectionTarget, int id, int style) {
		super("", style); //$NON-NLS-1$

		fSite= site;
		fSelectionTarget= selectionTarget;
		fSelectedElements= new ArrayList<>();
		fListeners= new ArrayList<>();

		setId(Integer.toString(id));
    }

	/**
	 * A detailed description usable for a {@link FormText}
	 * depending on the current selection, or <code>null</code>
	 * if <code>!enabled()</code>
	 *
	 * @return A detailed description or null if <code>!enabled()</code>
	 */
	public abstract String getDetailedDescription();

	@Override
	public void selectionChanged(final SelectionChangedEvent event) {
		final ISelection selection = event.getSelection();
		if (selection instanceof IStructuredSelection) {
			setEnabled(canHandle((IStructuredSelection) selection));
			fSelectedElements.clear();
			fSelectedElements.addAll(((IStructuredSelection)selection).toList());
		} else {
			setEnabled(canHandle(StructuredSelection.EMPTY));
			fSelectedElements.clear();
		}
	}

	protected abstract boolean canHandle(IStructuredSelection elements);

	protected List<?> getSelectedElements() {
		return fSelectedElements;
	}

	public void addBuildpathModifierListener(IBuildpathModifierListener listener) {
		fListeners.add(listener);
	}

	public void removeBuildpathModifierListener(IBuildpathModifierListener listener) {
		fListeners.remove(listener);
	}

	protected void informListeners(BuildpathDelta delta) {
		for (Iterator<IBuildpathModifierListener> iterator= fListeners.iterator(); iterator.hasNext();) {
	        iterator.next().buildpathChanged(delta);
        }
	}

	protected Shell getShell() {
		if (fSite == null)
			return JavaPlugin.getActiveWorkbenchShell();

	    return fSite.getShell() != null ? fSite.getShell() : JavaPlugin.getActiveWorkbenchShell();
    }

	protected void showExceptionDialog(CoreException exception, String title) {
		showError(exception, getShell(), title, exception.getMessage());
	}

	protected void showError(CoreException e, Shell shell, String title, String message) {
		IStatus status= e.getStatus();
		if (status != null) {
			ErrorDialog.openError(shell, message, title, status);
		} else {
			MessageDialog.openError(shell, title, message);
		}
	}

	protected void selectAndReveal(final ISelection selection) {
		if (fSelectionTarget != null)
			fSelectionTarget.selectReveal(selection);

		if (fSite == null)
			return;

		// validate the input
		IWorkbenchPage page= fSite.getPage();
		if (page == null)
			return;

		// get all the view and editor parts
		List<IWorkbenchPart> parts= new ArrayList<>();
		for (IWorkbenchPartReference ref : page.getViewReferences()) {
			IWorkbenchPart part= ref.getPart(false);
			if (part != null)
				parts.add(part);
		}
		for (IWorkbenchPartReference ref : page.getEditorReferences()) {
			if (ref.getPart(false) != null) {
				parts.add(ref.getPart(false));
			}
		}

		Iterator<IWorkbenchPart> itr= parts.iterator();
		while (itr.hasNext()) {
			IWorkbenchPart part= itr.next();

			// get the part's ISetSelectionTarget implementation
			ISetSelectionTarget target= null;
			if (part instanceof ISetSelectionTarget)
				target= (ISetSelectionTarget) part;
			else
				target= part.getAdapter(ISetSelectionTarget.class);

			if (target != null) {
				// select and reveal resource
				final ISetSelectionTarget finalTarget= target;
				page.getWorkbenchWindow().getShell().getDisplay().asyncExec(new Runnable() {
					@Override
					public void run() {
						finalTarget.selectReveal(selection);
					}
				});
			}
		}
	}
}