//------------------------------------------------------------------------------
// Copyright (c) 2005, 2006 IBM Corporation and others.
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// which accompanies this distribution, and is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// Contributors:
// IBM Corporation - initial implementation
//------------------------------------------------------------------------------
package org.eclipse.epf.authoring.ui.dnd;

import org.eclipse.emf.edit.command.DragAndDropCommand;
import org.eclipse.emf.edit.command.DragAndDropFeedback;
import org.eclipse.emf.edit.domain.EditingDomain;
import org.eclipse.emf.edit.ui.dnd.EditingDomainViewerDropAdapter;
import org.eclipse.jface.viewers.TableTreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.DropTargetEvent;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.swt.widgets.Widget;

/**
 * @author Phong Nguyen Le
 * @since 1.0
 */
public class EditingDomainTableTreeViewerDropAdapter extends
		EditingDomainViewerDropAdapter {

	public EditingDomainTableTreeViewerDropAdapter(EditingDomain domain,
			Viewer viewer) {
		super(domain, viewer);
	}

	public void dragEnter(DropTargetEvent event) {
		if (event.detail == DND.DROP_NONE) {
			event.detail = DND.DROP_COPY;
		}
		super.dragEnter(event);
	}

	public void dragOver(DropTargetEvent event) {
		if (event.detail == DND.DROP_NONE) {
			event.detail = DND.DROP_COPY;
		}

		super.dragOver(event);
	}

	public void dropAccept(DropTargetEvent event) {
		if (event.detail == DND.DROP_NONE) {
			event.detail = DND.DROP_COPY;
		}

		super.dropAccept(event);
	}

	protected void helper(DropTargetEvent event) {
		Table table = null;
		TableItem item = null;
		if (viewer instanceof TableTreeViewer) {
			table = ((TableTreeViewer) viewer).getTableTree().getTable();
			item = table.getItem(table.toControl(event.x, event.y));
			event.item = item;
		}

		superHelper(event);

		if (item != null && (event.feedback & DND.FEEDBACK_SELECT) != 0) {
			table.setSelection(new TableItem[] { item });
		}
	}

	protected Object getDropTarget(Widget item) {
		return EditingDomainTableTreeViewerDropAdapter.extractDropTarget(item);
	}

	/**
	 * This is called to indicate that the drop action should be invoked.
	 */
	public void drop(DropTargetEvent event) {
		// A command was created if the source was available early, and the
		// information used to create it was cached...
		if (dragAndDropCommandInformation != null) {
			// Recreate the command.
			command = dragAndDropCommandInformation.createCommand();
		} else {
			// Otherwise, the source should be available now as event.data, and
			// we can create the command.
			source = extractDragSource(event.data);
			Object target = getDropTarget(event.item);
			command = DragAndDropCommand.create(domain, target,
					getLocation(event), event.operations, originalOperation,
					source);
		}

		// If the command can execute...
		if (command.canExecute()) {
			// Execute it.
			domain.getCommandStack().execute(command);
		} else {
			// Otherwise, let's call the whole thing off.
			event.detail = DND.DROP_NONE;
			command.dispose();
		}

		// Clean up the state.
		command = null;
		commandTarget = null;
		source = null;
	}

	/**
	 * This method is called the same way for each of the
	 * {@link org.eclipse.swt.dnd.DropTargetListener} methods, except for leave
	 * and drop. If the source is available early, it creates or revalidates the
	 * {@link DragAndDropCommand}, and updates the event's detail (operation)
	 * and feedback (drag under effect), appropriately.
	 */
	protected void superHelper(DropTargetEvent event) {
		// If we can't do anything else, we'll provide the default select
		// feedback and enable auto-scroll and auto-expand effects.
		event.feedback = DND.FEEDBACK_SELECT | getAutoFeedback();

		// If we don't already have it, try to get the source early. We can't
		// give feedback if it's not available yet (this is platform-dependent).
		if (source == null) {
			source = getDragSource(event);
			if (source == null)
				return;
		}

		// Get the target object from the item widget and the mouse location in
		// it.
		Object target = getDropTarget(event.item);
		float location = getLocation(event);

		// Determine if we can create a valid command at the current location.
		boolean valid = false;

		// If we don't have a previous cached command...
		if (command == null) {
			// We'll need to keep track of the information we use to create the
			// command, so that we can recreate it in drop.
			dragAndDropCommandInformation = new DragAndDropCommandInformation(
					domain, target, location, event.operations,
					originalOperation, source);

			// Remember the target; create the command and test if it is
			// executable.
			commandTarget = target;
			command = dragAndDropCommandInformation.createCommand();
			valid = command.canExecute();
		} else {
			// Check if the cached command can provide DND
			// feedback/revalidation.
			if (target == commandTarget
					&& command instanceof DragAndDropFeedback) {
				// If so, revalidate the command.
				valid = ((DragAndDropFeedback) command).validate(target,
						location, event.operations, originalOperation, source);

				// Keep track of any changes to the command information.
				dragAndDropCommandInformation = new DragAndDropCommandInformation(
						domain, target, location, event.operations,
						originalOperation, source);
			} else {
				// If not, dispose the current command and create a new one.
				dragAndDropCommandInformation = new DragAndDropCommandInformation(
						domain, target, location, event.operations,
						originalOperation, source);
				commandTarget = target;
				command.dispose();
				command = dragAndDropCommandInformation.createCommand();
				valid = command.canExecute();
			}
		}

		// If this command can provide detailed drag and drop feedback...
		if (command instanceof DragAndDropFeedback) {
			// Use it for the operation and drag under effect.
			DragAndDropFeedback dragAndDropFeedback = (DragAndDropFeedback) command;
			event.detail = dragAndDropFeedback.getOperation();
			event.feedback = dragAndDropFeedback.getFeedback()
					| getAutoFeedback();
		} else if (!valid) {
			// There is no executable command, so we'd better nix the whole
			// deal.
			event.detail = DND.DROP_NONE;
		}
	}

}
