/*******************************************************************************
 * Copyright (c) 2004, 2009 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
 *******************************************************************************/

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

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

import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.IJobChangeEvent;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.core.runtime.jobs.JobChangeAdapter;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.dialogs.MessageDialogWithToggle;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.mylyn.commons.core.StatusHandler;
import org.eclipse.mylyn.context.core.ContextCore;
import org.eclipse.mylyn.internal.provisional.commons.ui.CommonUiUtil;
import org.eclipse.mylyn.internal.provisional.commons.ui.ICoreRunnable;
import org.eclipse.mylyn.internal.provisional.commons.ui.WorkbenchUtil;
import org.eclipse.mylyn.internal.tasks.core.AbstractTask;
import org.eclipse.mylyn.internal.tasks.core.AutomaticRepositoryTaskContainer;
import org.eclipse.mylyn.internal.tasks.core.ITaskListRunnable;
import org.eclipse.mylyn.internal.tasks.core.LocalTask;
import org.eclipse.mylyn.internal.tasks.core.RepositoryQuery;
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.core.sync.DeleteTasksJob;
import org.eclipse.mylyn.internal.tasks.ui.TasksUiPlugin;
import org.eclipse.mylyn.internal.tasks.ui.util.TasksUiInternal;
import org.eclipse.mylyn.tasks.core.AbstractRepositoryConnector;
import org.eclipse.mylyn.tasks.core.IRepositoryElement;
import org.eclipse.mylyn.tasks.core.IRepositoryQuery;
import org.eclipse.mylyn.tasks.core.ITask;
import org.eclipse.mylyn.tasks.core.TaskRepository;
import org.eclipse.mylyn.tasks.ui.TasksUi;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.actions.BaseSelectionListenerAction;
import org.eclipse.ui.internal.WorkbenchImages;
import org.eclipse.ui.texteditor.IWorkbenchActionDefinitionIds;

/**
 * @author Mik Kersten
 */
public class DeleteAction extends BaseSelectionListenerAction {

	public static final String ID = "org.eclipse.mylyn.tasklist.actions.delete"; //$NON-NLS-1$

	public DeleteAction() {
		super(Messages.DeleteAction_Delete);
		setId(ID);
		setImageDescriptor(WorkbenchImages.getImageDescriptor(ISharedImages.IMG_TOOL_DELETE));
		setActionDefinitionId(IWorkbenchActionDefinitionIds.DELETE);
	}

	@Override
	public void run() {
		doDelete(getStructuredSelection().toList());
	}

	protected void doDelete(final List<?> toDelete) {

		boolean allLocalTasks = true;
		boolean allSupportRepositoryDeletion = true;
		boolean allElementsAreTasks = true;

		// determine what repository elements are to be deleted so that we can present the correct message to the user
		for (Object object : toDelete) {
			if (object instanceof ITask) {
				ITask task = (ITask) object;
				AbstractRepositoryConnector repositoryConnector = TasksUi.getRepositoryConnector(task.getConnectorKind());
				TaskRepository repository = TasksUi.getRepositoryManager().getRepository(task.getConnectorKind(),
						task.getRepositoryUrl());
				if (repository != null && repositoryConnector != null) {
					allSupportRepositoryDeletion &= repositoryConnector.canDeleteTask(repository, task);
				} else {
					allSupportRepositoryDeletion = false;
				}
				allLocalTasks &= task instanceof LocalTask;
			} else {
				allElementsAreTasks = false;
			}
		}

		String elements = buildElementListString(toDelete);
		String message = buildMessage(toDelete, elements, allElementsAreTasks);

		if (toDelete.isEmpty()) {
			MessageDialog.openError(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(),
					Messages.DeleteAction_Delete_failed, Messages.DeleteAction_Nothing_selected);
		} else {
			boolean deleteConfirmed = false;
			boolean deleteOnServer = false;

			final boolean allTasksDeletable = allSupportRepositoryDeletion;

			if (allLocalTasks || !allElementsAreTasks) {
				deleteConfirmed = MessageDialog.openQuestion(PlatformUI.getWorkbench()
						.getActiveWorkbenchWindow()
						.getShell(), Messages.DeleteAction_Delete_Tasks, message);
			} else {
				String toggleMessage = Messages.DeleteAction_Also_delete_from_repository_X;
				if (allTasksDeletable) {
					toggleMessage = NLS.bind(toggleMessage, ""); //$NON-NLS-1$
				} else {
					toggleMessage = NLS.bind(toggleMessage, Messages.DeleteAction_Not_supported);
				}
				final MessageDialogWithToggle dialog = new MessageDialogWithToggle(WorkbenchUtil.getShell(),
						Messages.DeleteAction_Delete_Tasks, null, message, MessageDialog.QUESTION, new String[] {
								IDialogConstants.YES_LABEL, IDialogConstants.NO_LABEL }, 0, toggleMessage, false) {
					@Override
					protected Control createContents(Composite parent) {
						Control createdControl = super.createContents(parent);
						getToggleButton().setEnabled(allTasksDeletable);
						return createdControl;
					}
				};

				deleteConfirmed = dialog.open() == IDialogConstants.YES_ID;
				deleteOnServer = dialog.getToggleState() && allTasksDeletable;
			}

			if (deleteConfirmed) {
				deleteElements(toDelete, deleteOnServer);
			}
		}
	}

	private void deleteElements(final List<?> toDelete, final boolean deleteOnServer) {
		ICoreRunnable op = new ICoreRunnable() {
			public void run(IProgressMonitor monitor) throws CoreException {
				try {
					monitor.beginTask(Messages.DeleteAction_Delete_in_progress, IProgressMonitor.UNKNOWN);
					prepareDeletion(toDelete);
					TasksUiPlugin.getTaskList().run(new ITaskListRunnable() {
						public void execute(IProgressMonitor monitor) throws CoreException {
							performDeletion(toDelete);
							if (deleteOnServer) {
								performDeletionFromServer(toDelete);
							}
						}

					}, monitor);
				} finally {
					monitor.done();
				}
			}
		};
		try {
			CommonUiUtil.runInUi(op, null);
		} catch (CoreException e) {
			Status status = new Status(IStatus.ERROR, TasksUiPlugin.ID_PLUGIN, NLS.bind(
					"Problems encountered deleting task list elements: {0}", e.getMessage()), e); //$NON-NLS-1$
			TasksUiInternal.logAndDisplayStatus(Messages.DeleteTaskRepositoryAction_Delete_Task_Repository_Failed,
					status);
		} catch (OperationCanceledException e) {
			// canceled
		}
	}

	private String buildMessage(final List<?> toDelete, String elements, boolean allElementsAreTasks) {
		String message;

		if (toDelete.size() == 1) {
			Object object = toDelete.get(0);
			if (object instanceof ITask) {
				if ((AbstractTask) object instanceof LocalTask) {
					message = Messages.DeleteAction_Permanently_delete_from_task_list;
				} else {
					message = Messages.DeleteAction_Delete_task_from_task_list_context_planning_deleted;
				}

			} else if (object instanceof TaskCategory) {
				message = Messages.DeleteAction_Permanently_delete_the_category;
			} else if (object instanceof IRepositoryQuery) {
				message = Messages.DeleteAction_Permanently_delete_the_query;
			} else if (object instanceof UnmatchedTaskContainer) {
				message = Messages.DeleteAction_Delete_the_planning_information_and_context_of_all_unmatched_tasks;
			} else if (object instanceof UnsubmittedTaskContainer) {
				message = Messages.DeleteAction_Delete_all_of_the_unsubmitted_tasks;
			} else {
				message = Messages.DeleteAction_Permanently_delete_the_element_listed_below;
			}
		} else {
			if (allElementsAreTasks) {
				message = Messages.DeleteAction_Delete_tasks_from_task_list_context_planning_deleted;
			} else {
				message = Messages.DeleteAction_Delete_elements_from_task_list_context_planning_deleted;
			}
		}
		message += "\n\n" + elements; //$NON-NLS-1$
		return message;
	}

	private String buildElementListString(final List<?> toDelete) {
		String elements = ""; //$NON-NLS-1$
		int i = 0;
		for (Object object : toDelete) {
			if (object instanceof UnmatchedTaskContainer) {
				continue;
			}

			i++;
			if (i < 20) {
				// TODO this action should be based on the action enablement and check if the container is user managed or not
				if (object instanceof IRepositoryElement) {
					elements += "    " + ((IRepositoryElement) object).getSummary() + "\n"; //$NON-NLS-1$ //$NON-NLS-2$
				}
			} else {
				elements += "..."; //$NON-NLS-1$
				break;
			}
		}
		return elements;
	}

	public static void prepareDeletion(Collection<?> toDelete) {
		for (Object selectedObject : toDelete) {
			if (selectedObject instanceof AbstractTask) {
				AbstractTask task = (AbstractTask) selectedObject;
				TasksUi.getTaskActivityManager().deactivateTask(task);
				TasksUiInternal.closeTaskEditorInAllPages(task, false);
			} else if (selectedObject instanceof AutomaticRepositoryTaskContainer) {
				// support both the unmatched and the unsubmitted
				if (toDelete.size() == 1) {
					prepareDeletion(((AutomaticRepositoryTaskContainer) selectedObject).getChildren());
				}
			}
		}
	}

	public static void performDeletion(Collection<?> toDelete) {
		for (Object selectedObject : toDelete) {
			if (selectedObject instanceof AbstractTask) {
				AbstractTask task = (AbstractTask) selectedObject;
				TasksUiInternal.getTaskList().deleteTask(task);
				try {
					TasksUiPlugin.getTaskDataManager().deleteTaskData(task);
				} catch (CoreException e) {
					StatusHandler.log(new Status(IStatus.ERROR, TasksUiPlugin.ID_PLUGIN, "Failed to delete task data", //$NON-NLS-1$
							e));
				}
				ContextCore.getContextManager().deleteContext(task.getHandleIdentifier());
			} else if (selectedObject instanceof IRepositoryQuery) {
				TasksUiInternal.getTaskList().deleteQuery((RepositoryQuery) selectedObject);
			} else if (selectedObject instanceof TaskCategory) {
				TasksUiInternal.getTaskList().deleteCategory((TaskCategory) selectedObject);
			} else if (selectedObject instanceof AutomaticRepositoryTaskContainer) {
				// support both the unmatched and the unsubmitted
				if (toDelete.size() == 1) {
					// loop to ensure that all subtasks are deleted as well
					for (int i = 0; i < 5; i++) {
						Collection<ITask> children = ((AutomaticRepositoryTaskContainer) selectedObject).getChildren();
						if (children.isEmpty()) {
							break;
						}
						performDeletion(children);
					}
				}
			}
		}
	}

	private void performDeletionFromServer(List<?> toDelete) {
		List<ITask> tasksToDelete = new ArrayList<ITask>();
		for (Object element : toDelete) {
			if (element instanceof ITask) {
				tasksToDelete.add((ITask) element);
			}

		}
		final DeleteTasksJob deleteTaskJob = new DeleteTasksJob(Messages.DeleteAction_Deleting_tasks_from_repositories,
				tasksToDelete, TasksUi.getRepositoryManager());
		deleteTaskJob.setPriority(Job.INTERACTIVE);
		deleteTaskJob.addJobChangeListener(new JobChangeAdapter() {
			@Override
			public void done(IJobChangeEvent event) {
				if (deleteTaskJob.getStatus() != null && !deleteTaskJob.getStatus().isOK()) {
					StatusHandler.log(deleteTaskJob.getStatus());
				}
			}
		});
		deleteTaskJob.setUser(true);
		deleteTaskJob.schedule();
	}

	@Override
	protected boolean updateSelection(IStructuredSelection selection) {
		List<?> elements = (selection).toList();
		for (Object object : elements) {
			if (object instanceof UncategorizedTaskContainer) {
				return false;
			}
		}
		return true;
	}

}
