/*******************************************************************************
 * 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
 *     Ken Sueda - initial prototype
 *******************************************************************************/

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

import java.util.Collection;
import java.util.Set;

import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.ISafeRunnable;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.SafeRunner;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.action.ControlContribution;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.mylyn.commons.core.StatusHandler;
import org.eclipse.mylyn.commons.ui.CommonImages;
import org.eclipse.mylyn.commons.ui.SelectionProviderAdapter;
import org.eclipse.mylyn.commons.workbench.editors.CommonTextSupport;
import org.eclipse.mylyn.internal.tasks.core.AbstractTask;
import org.eclipse.mylyn.internal.tasks.core.ITaskListChangeListener;
import org.eclipse.mylyn.internal.tasks.core.ITasksCoreConstants;
import org.eclipse.mylyn.internal.tasks.core.LocalTask;
import org.eclipse.mylyn.internal.tasks.core.TaskContainerDelta;
import org.eclipse.mylyn.internal.tasks.ui.TasksUiPlugin;
import org.eclipse.mylyn.internal.tasks.ui.actions.NewSubTaskAction;
import org.eclipse.mylyn.internal.tasks.ui.util.TasksUiInternal;
import org.eclipse.mylyn.tasks.core.AbstractRepositoryConnector;
import org.eclipse.mylyn.tasks.core.ITask;
import org.eclipse.mylyn.tasks.core.ITaskActivityListener;
import org.eclipse.mylyn.tasks.core.TaskRepository;
import org.eclipse.mylyn.tasks.core.data.TaskData;
import org.eclipse.mylyn.tasks.ui.ITasksUiConstants;
import org.eclipse.mylyn.tasks.ui.TasksUi;
import org.eclipse.mylyn.tasks.ui.editors.TaskEditor;
import org.eclipse.mylyn.tasks.ui.editors.TaskEditorInput;
import org.eclipse.mylyn.tasks.ui.editors.TaskFormPage;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.IPropertyListener;
import org.eclipse.ui.IWorkbenchPartConstants;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.forms.IFormPart;
import org.eclipse.ui.forms.IManagedForm;
import org.eclipse.ui.forms.widgets.FormToolkit;
import org.eclipse.ui.handlers.IHandlerService;

/**
 * @author Mik Kersten
 * @author Rob Elves
 * @author Shawn Minto
 * @author Steffen Pingel
 */
public class TaskPlanningEditor extends TaskFormPage {

	private Composite editorComposite;

	private TaskRepository repository;

	private AbstractTask task;

	private Button saveButton;

	private final IPropertyListener dirtyStateListener = new IPropertyListener() {

		public void propertyChanged(Object source, int propId) {
			if (propId == IWorkbenchPartConstants.PROP_DIRTY && saveButton != null) {
				saveButton.setEnabled(getEditor().isDirty());
			}
		}
	};

	private final ITaskListChangeListener taskListChangeListener = new TaskListChangeAdapter() {
		@Override
		public void containersChanged(Set<TaskContainerDelta> containers) {
			for (TaskContainerDelta taskContainerDelta : containers) {
				if (taskContainerDelta.getElement() instanceof ITask) {
					final AbstractTask updateTask = (AbstractTask) taskContainerDelta.getElement();
					if (updateTask != null && task != null
							&& updateTask.getHandleIdentifier().equals(task.getHandleIdentifier())) {
						if (PlatformUI.getWorkbench() != null && !PlatformUI.getWorkbench().isClosing()) {
							PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {
								public void run() {
									refresh();
								}
							});
						}
						break;
					}
				}
			}
		}

	};

	private CommonTextSupport textSupport;

	private ITaskActivityListener timingListener;

	private FormToolkit toolkit;

	private FocusTracker focusTracker;

	public TaskPlanningEditor(TaskEditor editor) {
		super(editor, ITasksUiConstants.ID_PAGE_PLANNING, Messages.TaskPlanningEditor_Planning);
		TasksUiInternal.getTaskList().addChangeListener(taskListChangeListener);
	}

	private void createContributions(final Composite editorComposite) {
		Collection<LocalTaskEditorContributionDescriptor> localEditorContributions = TaskEditorContributionExtensionReader.getLocalEditorContributions();
		for (final LocalTaskEditorContributionDescriptor descriptor : localEditorContributions) {
			SafeRunner.run(new ISafeRunnable() {
				public void handleException(Throwable e) {
					StatusHandler.log(new Status(IStatus.ERROR, TasksUiPlugin.ID_PLUGIN,
							"Error creating task editor contribution: \"" + descriptor.getId() + "\"", e)); //$NON-NLS-1$ //$NON-NLS-2$
				}

				public void run() throws Exception {
					AbstractLocalEditorPart part = descriptor.createPart();
					initializePart(editorComposite, part);
				}

			});
		}
	}

	@Override
	protected void createFormContent(IManagedForm managedForm) {
		super.createFormContent(managedForm);

		TaskEditorInput taskEditorInput = (TaskEditorInput) getEditorInput();
		task = (AbstractTask) taskEditorInput.getTask();
		repository = TasksUi.getRepositoryManager().getRepository(task.getConnectorKind(), task.getRepositoryUrl());

		toolkit = managedForm.getToolkit();

		editorComposite = managedForm.getForm().getBody();
		GridLayout layout = new GridLayout();
		layout.verticalSpacing = 0;
		editorComposite.setLayout(layout);

		if (task instanceof LocalTask) {
			SummaryPart part = new SummaryPart();
			part.setTextSupport(textSupport);
			initializePart(editorComposite, part);
			initializePart(editorComposite, new AttributePart());
			// currently there is only one location for extensions
			createContributions(editorComposite);
		}

		PlanningPart planningPart = new PlanningPart(SWT.NONE);
		// disable due date picker if it's a repository due date
		boolean needsDueDate = true;
		if (task != null) {
			try {
				TaskData taskData = TasksUi.getTaskDataManager().getTaskData(task);
				if (taskData != null) {
					AbstractRepositoryConnector connector = TasksUi.getRepositoryConnector(taskData.getConnectorKind());
					TaskRepository taskRepository = TasksUi.getRepositoryManager().getRepository(
							taskData.getConnectorKind(), taskData.getRepositoryUrl());
					if (connector != null && taskRepository != null
							&& connector.hasRepositoryDueDate(taskRepository, task, taskData)) {
						needsDueDate = false;
					}
				}
			} catch (CoreException e) {
				// ignore
			}
		}
		planningPart.initialize(getManagedForm(), repository, task, needsDueDate, this, textSupport);
		planningPart.createControl(editorComposite, toolkit);
		planningPart.getSection().setLayoutData(new GridData(GridData.FILL_BOTH));
		getManagedForm().addPart(planningPart);

		focusTracker = new FocusTracker();
		focusTracker.track(editorComposite);
	}

	@Override
	public void dispose() {
		getEditor().removePropertyListener(dirtyStateListener);
		if (timingListener != null) {
			TasksUiPlugin.getTaskActivityManager().removeActivityListener(timingListener);
		}
		TasksUiInternal.getTaskList().removeChangeListener(taskListChangeListener);
		super.dispose();
	}

	@Override
	public void doSave(IProgressMonitor monitor) {
		super.doSave(monitor);
		// update task title
		TasksUiInternal.getTaskList().notifyElementChanged(task);
	}

	@Override
	public void doSaveAs() {
		// don't support saving as
	}

	/**
	 * Override for customizing the tool bar.
	 */
	@Override
	public void fillToolBar(IToolBarManager toolBarManager) {
		TaskEditorInput taskEditorInput = (TaskEditorInput) getEditorInput();
		ITask task = taskEditorInput.getTask();
		if (task instanceof LocalTask
				&& task.getAttribute(ITasksCoreConstants.ATTRIBUTE_OUTGOING_NEW_CONNECTOR_KIND) == null) {

			NewSubTaskAction newSubTaskAction = new NewSubTaskAction();
			newSubTaskAction.selectionChanged(newSubTaskAction, new StructuredSelection(task));
			if (newSubTaskAction.isEnabled()) {
				toolBarManager.add(newSubTaskAction);
			}
		}
	}

	@Override
	public TaskEditor getEditor() {
		return (TaskEditor) super.getEditor();
	}

	@Override
	public void init(IEditorSite site, IEditorInput input) {
		super.init(site, input);
		this.textSupport = new CommonTextSupport((IHandlerService) getSite().getService(IHandlerService.class));
		this.textSupport.setSelectionChangedListener((TaskEditorActionContributor) getEditorSite().getActionBarContributor());

		site.setSelectionProvider(new SelectionProviderAdapter(new StructuredSelection(
				((TaskEditorInput) input).getTask())));
	}

	private void initializePart(final Composite editorComposite, final AbstractLocalEditorPart part) {
		part.initialize(getManagedForm(), repository, task);
		Control control = part.createControl(editorComposite, toolkit);
		part.setControl(control);
		GridDataFactory.fillDefaults().grab(true, false).applyTo(control);
		getManagedForm().addPart(part);
	}

	@Override
	public boolean isSaveAsAllowed() {
		return false;
	}

	@Override
	public void refresh() {
		if (getManagedForm() == null || getManagedForm().getForm().isDisposed()) {
			// editor possibly closed
			return;
		}

		getEditor().updateHeaderToolBar();
		IFormPart[] parts = getManagedForm().getParts();
		// refresh will not be invoked unless parts are stale
		for (IFormPart part : parts) {
			if (part instanceof AbstractLocalEditorPart) {
				((AbstractLocalEditorPart) part).refresh(false);
			} else {
				part.refresh();
			}
		}
		getManagedForm().reflow(true);
	}

	public void fillLeftHeaderToolBar(IToolBarManager toolBarManager) {
		if (getEditorInput() instanceof TaskEditorInput
				&& ((TaskEditorInput) getEditorInput()).getTask() instanceof LocalTask) {
			ControlContribution submitButtonContribution = new ControlContribution(
					"org.eclipse.mylyn.tasks.toolbars.save") { //$NON-NLS-1$

				@Override
				protected Control createControl(Composite parent) {
					saveButton = new Button(parent, SWT.FLAT);
					saveButton.setText(Messages.TaskPlanningEditor_Save);
					saveButton.setImage(CommonImages.getImage(CommonImages.SAVE));
					saveButton.setBackground(null);
					saveButton.addListener(SWT.Selection, new Listener() {
						public void handleEvent(Event e) {
							doSave(new NullProgressMonitor());
						}
					});
					saveButton.setEnabled(getEditor().isDirty());

					return saveButton;
				}
			};
			getEditor().addPropertyListener(dirtyStateListener);
			toolBarManager.add(submitButtonContribution);
		}
	}

	@Override
	public void setFocus() {
		if (focusTracker.setFocus()) {
			return;
		} else {
			IFormPart[] parts = getManagedForm().getParts();
			if (parts.length > 0) {
				parts[0].setFocus();
				return;
			}
		}
		super.setFocus();
	}

}
