/*******************************************************************************
 * Copyright (c) 2004, 2010 Tasktop Technologies 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:
 *     Tasktop Technologies - initial API and implementation
 *     Jevgeni Holodkov - improvements
 *******************************************************************************/

package org.eclipse.mylyn.internal.tasks.ui.views;

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

import org.eclipse.jface.util.LocalSelectionTransfer;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerDropAdapter;
import org.eclipse.mylyn.internal.tasks.core.AbstractTask;
import org.eclipse.mylyn.internal.tasks.core.AbstractTaskCategory;
import org.eclipse.mylyn.internal.tasks.core.AbstractTaskContainer;
import org.eclipse.mylyn.internal.tasks.core.DateRange;
import org.eclipse.mylyn.internal.tasks.core.LocalTask;
import org.eclipse.mylyn.internal.tasks.core.ScheduledTaskContainer;
import org.eclipse.mylyn.internal.tasks.core.TaskCategory;
import org.eclipse.mylyn.internal.tasks.core.UncategorizedTaskContainer;
import org.eclipse.mylyn.internal.tasks.core.UnmatchedTaskContainer;
import org.eclipse.mylyn.internal.tasks.core.UnsubmittedTaskContainer;
import org.eclipse.mylyn.internal.tasks.ui.TasksUiPlugin;
import org.eclipse.mylyn.internal.tasks.ui.util.AbstractRetrieveTitleFromUrlJob;
import org.eclipse.mylyn.internal.tasks.ui.util.TasksUiInternal;
import org.eclipse.mylyn.internal.tasks.ui.views.TaskScheduleContentProvider.StateTaskContainer;
import org.eclipse.mylyn.internal.tasks.ui.views.TaskScheduleContentProvider.Unscheduled;
import org.eclipse.mylyn.tasks.core.AbstractRepositoryConnector;
import org.eclipse.mylyn.tasks.core.ITask;
import org.eclipse.mylyn.tasks.core.ITaskContainer;
import org.eclipse.mylyn.tasks.ui.TaskDropListener.Operation;
import org.eclipse.mylyn.tasks.ui.TasksUiUtil;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.FileTransfer;
import org.eclipse.swt.dnd.TextTransfer;
import org.eclipse.swt.dnd.TransferData;
import org.eclipse.swt.dnd.URLTransfer;
import org.eclipse.ui.PlatformUI;

/**
 * @author Mik Kersten
 * @author Rob Elves (added URL based task creation support)
 * @author Jevgeni Holodkov
 * @author Sam Davis
 */
public class TaskListDropAdapter extends ViewerDropAdapter {

	private boolean fileTransfer;

	private boolean localTransfer;

	public TaskListDropAdapter(Viewer viewer) {
		super(viewer);
		setFeedbackEnabled(true);
	}

	@Override
	public boolean performDrop(final Object data) {
		List<ITask> tasksToMove = new ArrayList<ITask>();
		if (localTransfer) {
			ISelection selection = LocalSelectionTransfer.getTransfer().getSelection();
			List<ITask> tasks = TasksUiInternal.getTasksFromSelection(selection);
			tasksToMove.addAll(tasks);
		} else if (fileTransfer) {
			// TODO implement dropping of files
		} else if (data instanceof String) {
			String text = (String) data;
			AbstractTask task = createTaskFromUrl(text);
			if (task == null) {
				task = TasksUiInternal.createNewLocalTask(text);
			}
			if (task != null) {
				tasksToMove.add(task);
				final ITask newTask = task;
				PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {
					public void run() {
						TasksUiUtil.openTask(newTask);
					}
				});
			}
		}

		Object currentTarget = getCurrentTarget();
		if (currentTarget instanceof LocalTask && areAllLocalTasks(tasksToMove) && getCurrentLocation() == LOCATION_ON) {
			for (ITask task : tasksToMove) {
				if (!((AbstractTask) task).contains(((LocalTask) currentTarget).getHandleIdentifier())) {
					TasksUiInternal.getTaskList().addTask(task, (LocalTask) currentTarget);
				}
			}
		} else {
			if (currentTarget instanceof ITask && getCurrentLocation() == ViewerDropAdapter.LOCATION_ON
					&& getCurrentOperation() != DND.DROP_MOVE) {
				TasksUiInternal.getTaskDropHandler().loadTaskDropListeners();
				Operation operation = (getCurrentOperation() == DND.DROP_COPY) ? Operation.COPY : Operation.LINK;
				TasksUiInternal.getTaskDropHandler().fireTaskDropped(tasksToMove, (ITask) currentTarget, operation);
			} else {
				for (ITask task : tasksToMove) {
					if (currentTarget instanceof UncategorizedTaskContainer) {
						moveTask(task, (UncategorizedTaskContainer) currentTarget);
					} else if (currentTarget instanceof TaskCategory) {
						moveTask(task, (TaskCategory) currentTarget);
					} else if (currentTarget instanceof UnmatchedTaskContainer) {
						if (((UnmatchedTaskContainer) currentTarget).getRepositoryUrl().equals(task.getRepositoryUrl())) {
							moveTask(task, (AbstractTaskCategory) currentTarget);
						}
					} else if (currentTarget instanceof ITask) {
						ITask targetTask = (ITask) currentTarget;
						TaskListView view = TaskListView.getFromActivePerspective();
						if ((getCurrentLocation() == LOCATION_BEFORE || getCurrentLocation() == LOCATION_AFTER)
								&& view != null && view.isScheduledPresentation()) {
							if (targetTask instanceof AbstractTask) {
								DateRange targetDate = ((AbstractTask) targetTask).getScheduledForDate();
								TasksUiPlugin.getTaskActivityManager().setScheduledFor((AbstractTask) task, targetDate);
							}
						} else {
							AbstractTaskCategory targetCategory = null;
							// TODO: TaskCategory only used what about AbstractTaskCategory descendants?
							ITaskContainer container = TaskCategory.getParentTaskCategory(targetTask);
							if (container instanceof TaskCategory || container instanceof UncategorizedTaskContainer) {
								targetCategory = (AbstractTaskCategory) container;
							} else if (container instanceof UnmatchedTaskContainer) {
								if (((UnmatchedTaskContainer) container).getRepositoryUrl().equals(
										task.getRepositoryUrl())) {
									targetCategory = (AbstractTaskCategory) container;
								}
							}
							if (targetCategory != null) {
								moveTask(task, targetCategory);
							}
						}
					} else if (currentTarget instanceof ScheduledTaskContainer) {
						ScheduledTaskContainer container = (ScheduledTaskContainer) currentTarget;
						if (container instanceof Unscheduled) {
							TasksUiPlugin.getTaskActivityManager().setScheduledFor((AbstractTask) task, null);
						} else if (isValidTarget(container)) {
							TasksUiPlugin.getTaskActivityManager().setScheduledFor((AbstractTask) task,
									container.getDateRange());
						}
					} else if (currentTarget == null) {
						moveTask(task, TasksUiPlugin.getTaskList().getDefaultCategory());
					}
				}
			}
		}

		if (tasksToMove.size() == 1) {
			getViewer().setSelection(new StructuredSelection(tasksToMove.get(0)));
		}
		return true;
	}

	private boolean isValidTarget(ScheduledTaskContainer container) {
		// ignore incoming, outgoing, completed
		return container instanceof Unscheduled || !(container instanceof StateTaskContainer);
	}

	private void moveTask(ITask task, AbstractTaskContainer container) {
		if (!isUnsubmittedTask(task)) {
			TasksUiInternal.getTaskList().addTask(task, container);
		}
	}

	private boolean isUnsubmittedTask(ITask task) {
		if (task instanceof AbstractTask) {
			for (AbstractTaskContainer parent : ((AbstractTask) task).getParentContainers()) {
				if (parent instanceof UnsubmittedTaskContainer) {
					return true;
				}
			}
		}
		return false;
	}

	private boolean areAllLocalTasks(List<ITask> tasksToMove) {
		for (ITask task : tasksToMove) {
			if (!(task instanceof LocalTask) || isUnsubmittedTask(task)) {
				return false;
			}
		}
		return true;
	}

	/**
	 * @param data
	 *            string containing url and title separated by <quote>\n</quote>
	 */
	private AbstractTask createTaskFromUrl(String data) {
		if (!data.startsWith("http://") && !data.startsWith("https://")) { //$NON-NLS-1$ //$NON-NLS-2$
			return null;
		}

		String[] urlTransfer = data.split("\n"); //$NON-NLS-1$
		if (urlTransfer.length > 0) {
			String url = urlTransfer[0];
			AbstractRepositoryConnector connector = TasksUiPlugin.getRepositoryManager()
					.getConnectorForRepositoryTaskUrl(url);
			if (connector != null) {
				// attempt to find task in task list
				String repositoryUrl = connector.getRepositoryUrlFromTaskUrl(url);
				String taskId = connector.getTaskIdFromTaskUrl(url);
				AbstractTask task = TasksUiInternal.getTask(repositoryUrl, taskId, url);
				if (task != null) {
					return task;
				}
				if (repositoryUrl != null && taskId != null) {
					// attempt to open task in background
					// TODO: consider attaching a listener to OpenRepsitoryTaskJob to move task to drop target
					TasksUiInternal.openRepositoryTask(connector.getConnectorKind(), repositoryUrl, taskId, null, 0);
				}
			} else {
				// create local task, using title of web page as a summary
				final String summary = Messages.TaskListDropAdapter__retrieving_from_URL_;
				final LocalTask newTask = TasksUiInternal.createNewLocalTask(summary);
				newTask.setUrl(url);
				AbstractRetrieveTitleFromUrlJob job = new AbstractRetrieveTitleFromUrlJob(url) {
					@Override
					protected void titleRetrieved(final String pageTitle) {
						// make sure summary was not changed in the mean time 
						if (newTask.getSummary().equals(summary)) {
							newTask.setSummary(pageTitle);
							TasksUiInternal.getTaskList().notifyElementChanged(newTask);
						}
					}
				};
				job.schedule();
				return newTask;
			}
		}
		return null;
	}

	@Override
	public boolean validateDrop(Object targetObject, int operation, TransferData transferType) {
		fileTransfer = false;
		localTransfer = false;
		if (FileTransfer.getInstance().isSupportedType(transferType)) {
			fileTransfer = true;
			// TODO handle all files
			return false;
		} else if (LocalSelectionTransfer.getTransfer().isSupportedType(transferType)) {
			localTransfer = true;
			Object target = getCurrentTarget();
			if (target instanceof UncategorizedTaskContainer || target instanceof TaskCategory
					|| target instanceof UnmatchedTaskContainer
					|| (target instanceof ScheduledTaskContainer && isValidTarget((ScheduledTaskContainer) target))) {
				return true;
			} else if (target instanceof ITaskContainer
					&& (getCurrentLocation() == ViewerDropAdapter.LOCATION_AFTER || getCurrentLocation() == ViewerDropAdapter.LOCATION_BEFORE)) {
				return true;
			} else if (target instanceof LocalTask && getCurrentLocation() == ViewerDropAdapter.LOCATION_ON) {
				return true;
			} else if (target instanceof ITask && getCurrentLocation() == ViewerDropAdapter.LOCATION_ON
					&& getCurrentOperation() != DND.DROP_MOVE) {
				return true;
			} else {
				return false;
			}
		} else if (URLTransfer.getInstance().isSupportedType(transferType)) {
			return true;
		}

		return TextTransfer.getInstance().isSupportedType(transferType);
	}
}
