/*******************************************************************************
 * Copyright (c) 2003, 2015 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.ui.navigator;

import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.util.LocalSelectionTransfer;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.swt.dnd.DragSourceListener;
import org.eclipse.swt.dnd.DropTargetEvent;
import org.eclipse.swt.dnd.DropTargetListener;
import org.eclipse.swt.dnd.TransferData;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.internal.navigator.NavigatorContentService;

/**
 * <p>
 * Used by the
 * <b>org.eclipse.ui.navigator.navigatorContent/navigatorContent/commonDropAdapter</b>
 * extension point to carry out pluggable drop operations.
 * </p>
 * <p>
 * Each {@link CommonDropAdapterAssistant} is contained by single content
 * extension. The opportunity for each assistant to handle the drop operation is
 * determined by the <b>possibleChildren</b> expression of the
 * <b>org.eclipse.ui.navigator.navigatorContent/navigatorContent</b> extension;
 * whenever every element in the drag set matches the <b>possibleChildren</b>
 * expression of an extension, it is eligible to handle the drop operation. This
 * initial set is further culled using the <b>possibleDropTargets</b> expression
 * of the <b>commonDropAdapter</b> using the current drop target.
 * </p>
 * <p>
 * If drag operations originate outside of Eclipse, then the set of eligible
 * drop adapters is determined based on the drop target (using the
 * <b>possibleDropTargets</b> expression). Each assistant can then indicate
 * whether {@link #isSupportedType(TransferData) the incoming type is
 * supported}.
 * <p>
 * Whenever a match is found, the assistant will be given an opportunity to
 * first {@link #validateDrop(Object, int, TransferData)}, and then if the
 * assistant returns true, the assist must
 * {@link #handleDrop(CommonDropAdapter, DropTargetEvent, Object)}. If multiple
 * assistants match the drop target, then the potential assistants are ordered
 * based on priority and their override relationships and given an opportunity
 * to validate the drop operation in turn. The first one to validate will have
 * the opportunty to carry out the drop.
 * </p>
 *
 * <p>
 * Clients may handle DND operations that begin and end in the current viewer by
 * overriding the following methods:
 * </p>
 * <ul>
 * <li>{@link #validateDrop(Object, int, TransferData)}: Indicate whether this
 * assistant can handle a drop onto the current viewer.</li>
 * <li>{@link #handleDrop(CommonDropAdapter, DropTargetEvent, Object)}: Handle
 * the drop operation onto the current viewer.</li>
 * </ul>
 * <p>
 * If a user originates a drag operation to another viewer that cannot handle
 * one of the available drag transfer types, drop assistants may handle the drop
 * operation for the target viewer. Clients must override :
 * </p>
 * <ul>
 * <li>{@link #validatePluginTransferDrop(IStructuredSelection, Object)}:
 * Indicate whether this assistant can handle the drop onto another viewer.
 * <li>{@link #handlePluginTransferDrop(IStructuredSelection, Object)}: Handle
 * the drop operation onto the other viewer.</li>
 * </ul>
 * <p>
 * Clients may subclass this.
 * </p>
 *
 * @see INavigatorDnDService
 * @see INavigatorDnDService#findCommonDropAdapterAssistants(Object,
 *      TransferData)
 * @since 3.2
 *
 */
public abstract class CommonDropAdapterAssistant {

	private INavigatorContentService contentService;

	private DropTargetEvent _currentEvent;

	private CommonDropAdapter _dropAdapter;

	/**
	 * Perform any necessary initialization using the
	 * {@link INavigatorContentService}.
	 *
	 *
	 * @param aContentService
	 *            The instance of {@link INavigatorContentService} that the
	 *            current CommonDropAdapterAssistant will be associated with
	 */
	public final void init(INavigatorContentService aContentService) {
		contentService = aContentService;
		doInit();
	}


	/**
	 * Override to perform any one-time initialization.
	 */
	protected void doInit() {

	}

	/**
	 * Validates dropping on the given object. This method is called whenever
	 * some aspect of the drop operation changes.
	 * <p>
	 * Subclasses must implement this method to define which drops make sense.
	 * If clients return true, then they will be allowed to handle the drop in
	 * {@link #handleDrop(CommonDropAdapter, DropTargetEvent, Object) }.
	 * </p>
	 *
	 * @param target
	 *            the object that the mouse is currently hovering over, or
	 *            <code>null</code> if the mouse is hovering over empty space
	 * @param operation
	 *            the current drag operation (copy, move, etc.)
	 * @param transferType
	 *            the current transfer type
	 * @return A status indicating whether the drop is valid.
	 */
	public abstract IStatus validateDrop(Object target, int operation,
			TransferData transferType);

	/**
	 * Carry out the DND operation.
	 *
	 * <p>
	 * Note: Contrary to the SWT {@link DropTargetListener} specification, you
	 * <i>must</i> make sure that the aDropTargetEvent.detail is not set to
	 * DND.DROP_MOVE unless actual work is required in the
	 * {@link DragSourceListener#dragFinished(org.eclipse.swt.dnd.DragSourceEvent)}
	 * to complete the operation (for example removing the moved file). In
	 * particular for the LocalSelectionTransfer case, DND.DROP_MOVE cannot be
	 * used as it will cause incorrect behavior in some existing drag handlers.
	 *
	 * In case of move operations where no action is required on the source side
	 * (e.g. LocalSelectionTransfer) you must set aDropTargetEvent.detail to
	 * DND.DROP_NONE to signal this to the drag source. Even though the SWT
	 * specification says this is canceling the drop, it is not really doing so,
	 * it is only preventing the DND.DROP_MOVE from being passed through to the
	 * dragFinished() method.
	 *
	 * @param aDropAdapter
	 *            The Drop Adapter contains information that has already been
	 *            parsed from the drop event.
	 * @param aDropTargetEvent
	 *            The drop target event.
	 * @param aTarget
	 *            The object being dragged onto
	 * @return A status indicating whether the drop completed OK.
	 */
	public abstract IStatus handleDrop(CommonDropAdapter aDropAdapter,
			DropTargetEvent aDropTargetEvent, Object aTarget);

	/**
	 * Clients may extend the supported transfer types beyond the default
	 * {@link LocalSelectionTransfer#getTransfer()} and
	 * {@link org.eclipse.ui.part.PluginTransfer#getInstance()} transfer types. When a transfer type
	 * other than one of these is encountered, the DND Service will query the
	 * <b>visible</b> and <b>active</b> descriptors that are <b>enabled</b>
	 * for the drop target of the current operation.
	 *
	 * @param aTransferType
	 *            The transfer data from the drop operation
	 * @return True if the given TransferData can be understood by this
	 *         assistant.
	 */
	public boolean isSupportedType(TransferData aTransferType) {
		return LocalSelectionTransfer.getTransfer().isSupportedType(
				aTransferType);
	}

	/**
	 *
	 * Return true if the client can handle the drop onto the target viewer of
	 * the drop operation.
	 * <p>
	 * The default behavior of this method is to return <b>Status.CANCEL_STATUS</b>.
	 * </p>
	 *
	 * @param aDragSelection
	 *            The selection dragged from the viewer.
	 * @param aDropTarget
	 *            The target of the drop operation.
	 *
	 * @return OK if the plugin transfer can be handled by this assistant.
	 */
	public IStatus validatePluginTransferDrop(
			IStructuredSelection aDragSelection, Object aDropTarget) {
		return Status.CANCEL_STATUS;
	}

	/**
	 * Handle the drop operation for the target viewer.
	 * <p>
	 * The default behavior of this method is to return <b>Status.CANCEL_STATUS</b>.
	 * </p>
	 *
	 * @param aDragSelection
	 *            The selection dragged from the viewer.
	 * @param aDropTarget
	 *            The target of the drop operation.
	 *
	 * @return OK if the drop operation succeeded.
	 */
	public IStatus handlePluginTransferDrop(
			IStructuredSelection aDragSelection, Object aDropTarget) {
		return Status.CANCEL_STATUS;
	}

	/**
	 *
	 * @return The associated content service.
	 */
	protected INavigatorContentService getContentService() {
		return contentService;
	}

	/**
	 *
	 * @return A shell for the viewer currently used by the
	 *         {@link INavigatorContentService}.
	 */
	protected final Shell getShell() {
		return ((NavigatorContentService) contentService).getShell();
	}

	/**
	 * Sets the current {@link DropTargetEvent}.
	 *
	 * This is used to make the event available to the client methods of this class.
	 *
	 * @param event
	 *            the new event.
	 *
	 * @since 3.4
	 */
	void setCurrentEvent(DropTargetEvent event) {
		_currentEvent = event;
	}

	/**
	 * Returns the current {@link DropTargetEvent}.
	 *
	 * @return event the current DropTargetEvent.
	 *
	 * @since 3.4
	 */
	public DropTargetEvent getCurrentEvent() {
		return _currentEvent;
	}

	/**
	 * Sets the {@link CommonDropAdapter}.
	 *
	 * @param dropAdapter the drop adapter
	 *
	 * @noreference This method is not intended to be referenced by clients.
	 * @nooverride This method is not intended to be re-implemented or extended by
	 *             clients.
	 */
	public void setCommonDropAdapter(CommonDropAdapter dropAdapter) {
		_dropAdapter = dropAdapter;
	}

	/**
	 * Returns the {@link CommonDropAdapter}.
	 *
	 * @return the CommonDropAdapter.
	 *
	 * @since 3.4
	 *
	 */
	protected CommonDropAdapter getCommonDropAdapter() {
		return _dropAdapter;
	}

}
