/*******************************************************************************
 * 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
 *******************************************************************************/

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

import java.util.Set;

import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.mylyn.internal.provisional.commons.ui.SelectionProviderAdapter;
import org.eclipse.mylyn.internal.tasks.core.ITaskListChangeListener;
import org.eclipse.mylyn.internal.tasks.core.TaskContainerDelta;
import org.eclipse.mylyn.internal.tasks.ui.actions.RepositoryElementActionGroup;
import org.eclipse.mylyn.internal.tasks.ui.util.TasksUiInternal;
import org.eclipse.mylyn.internal.tasks.ui.views.TaskListView;
import org.eclipse.mylyn.tasks.core.ITask;
import org.eclipse.mylyn.tasks.core.ITaskActivationListener;
import org.eclipse.mylyn.tasks.core.TaskActivationAdapter;
import org.eclipse.mylyn.tasks.ui.TasksUi;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.MenuDetectEvent;
import org.eclipse.swt.events.MenuDetectListener;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.ToolBar;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.forms.events.HyperlinkAdapter;
import org.eclipse.ui.forms.events.HyperlinkEvent;
import org.eclipse.ui.internal.ObjectActionContributorManager;
import org.eclipse.ui.internal.WorkbenchWindow;
import org.eclipse.ui.internal.layout.IWindowTrim;
import org.eclipse.ui.menus.WorkbenchWindowControlContribution;

/**
 * @author Mik Kersten
 * @author Leo Dos Santos
 */
public class TaskTrimWidget extends WorkbenchWindowControlContribution {

	public static String ID_CONTAINER = "org.eclipse.mylyn.tasks.ui.trim.container"; //$NON-NLS-1$

	public static String ID_CONTROL = "org.eclipse.mylyn.tasks.ui.trim.control"; //$NON-NLS-1$

	private Composite composite;

	private ITask activeTask;

	private MenuManager menuManager;

	private Menu menu;

	private TaskScalingHyperlink activeTaskLabel;

	private final ITaskActivationListener taskActivationListener = new TaskActivationAdapter() {

		@Override
		public void taskActivated(ITask task) {
			activeTask = task;
			indicateActiveTask();
		}

		@Override
		public void taskDeactivated(ITask task) {
			activeTask = null;
			indicateNoActiveTask();
		}

	};

	private final ITaskListChangeListener taskListListener = new ITaskListChangeListener() {
		public void containersChanged(Set<TaskContainerDelta> containers) {
			// update label in case task changes
			if (activeTask != null) {
				for (TaskContainerDelta taskContainerDelta : containers) {
					if (activeTask.equals(taskContainerDelta.getElement())) {
						if (taskContainerDelta.getKind().equals(TaskContainerDelta.Kind.CONTENT)) {
							Display.getDefault().asyncExec(new Runnable() {
								public void run() {
									if (activeTask != null && activeTask.isActive()) {
										indicateActiveTask();
									}
								}
							});
							return;
						}
					}
				}
			}
		}
	};

	private final IPropertyChangeListener preferencesListener = new IPropertyChangeListener() {
		public void propertyChange(PropertyChangeEvent event) {
			String property = event.getProperty();
			if (property.equals(ITasksUiPreferenceConstants.SHOW_TRIM)) {
				Object newValue = event.getNewValue();
				Boolean isVisible = false;
				if (newValue instanceof Boolean) {
					isVisible = (Boolean) newValue;
				} else if (newValue instanceof String) {
					isVisible = Boolean.parseBoolean((String) newValue);
				}
				setTrimVisible(isVisible);
			}
		}
	};

	private SelectionProviderAdapter activeTaskSelectionProvider;

	private RepositoryElementActionGroup actionGroup;

	public TaskTrimWidget() {
		TasksUi.getTaskActivityManager().addActivationListener(taskActivationListener);
		TasksUiPlugin.getTaskList().addChangeListener(taskListListener);
		TasksUiPlugin.getDefault().getPreferenceStore().addPropertyChangeListener(preferencesListener);
		hookContextMenu();
	}

	private void setTrimVisible(boolean visible) {
		IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
		if (window instanceof WorkbenchWindow) {
			IWindowTrim trim = ((WorkbenchWindow) window).getTrimManager().getTrim(ID_CONTAINER);
			if (trim != null) {
				((WorkbenchWindow) window).getTrimManager().setTrimVisible(trim, visible);
				((WorkbenchWindow) window).getTrimManager().forceLayout();
			}
		}
	}

	@Override
	public void dispose() {
		if (composite != null && !composite.isDisposed()) {
			composite.dispose();
		}
		composite = null;

		if (menuManager != null) {
			menuManager.removeAll();
			menuManager.dispose();
		}
		menuManager = null;

		if (menu != null && !menu.isDisposed()) {
			menu.dispose();
		}
		menu = null;

		actionGroup.setSelectionProvider(null);

		TasksUi.getTaskActivityManager().removeActivationListener(taskActivationListener);
		TasksUiPlugin.getTaskList().removeChangeListener(taskListListener);
		TasksUiPlugin.getDefault().getPreferenceStore().removePropertyChangeListener(preferencesListener);
	}

	@Override
	protected Control createControl(Composite parent) {
		composite = new Composite(parent, SWT.NONE);

		GridLayout layout = new GridLayout();
		layout.numColumns = 1;
		layout.horizontalSpacing = 0;
		layout.marginHeight = 0;
		layout.marginLeft = 0;
		layout.marginRight = 0;
		composite.setLayout(layout);

		composite.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, true));

		createStatusComposite(composite);

		if (!shouldShowTrim()) {
			if (parent instanceof ToolBar) {
				// bug 201589: it's not possible to hide the contribution on startup, as a work-around the tool bar is hidden which avoids flickering of the layout
				parent.setVisible(false);
			}
			// needs to be invoked asynchronously since the trim contribution is just getting constructed when createControl() is invoked
			parent.getDisplay().asyncExec(new Runnable() {
				public void run() {
					setTrimVisible(shouldShowTrim());
				}
			});
		}

		return composite;
	}

	private boolean shouldShowTrim() {
		IPreferenceStore uiPreferenceStore = TasksUiPlugin.getDefault().getPreferenceStore();
		return uiPreferenceStore.getBoolean(ITasksUiPreferenceConstants.SHOW_TRIM);
	}

	private Composite createStatusComposite(final Composite container) {
		GC gc = new GC(container);
		Point p = gc.textExtent("WWWWWWWWWWWWWWW"); //$NON-NLS-1$
		gc.dispose();

		activeTaskLabel = new TaskScalingHyperlink(container, SWT.RIGHT);
		// activeTaskLabel.setLayoutData(new GridData(p.x, SWT.DEFAULT));
		GridData gridData = new GridData(SWT.LEFT, SWT.CENTER, false, true);
		gridData.widthHint = p.x;
		gridData.minimumWidth = p.x;
		gridData.horizontalIndent = 0;
		activeTaskLabel.setLayoutData(gridData);
		activeTaskLabel.setText(Messages.TaskTrimWidget__no_task_active_);

		activeTask = TasksUi.getTaskActivityManager().getActiveTask();
		if (activeTask != null) {
			indicateActiveTask();
		}

		activeTaskLabel.addMenuDetectListener(new MenuDetectListener() {
			public void menuDetected(MenuDetectEvent e) {
				if (menu != null) {
					menu.dispose();
				}
				menu = menuManager.createContextMenu(container);
				menu.setVisible(true);
			}
		});

		activeTaskLabel.addHyperlinkListener(new HyperlinkAdapter() {
			@Override
			public void linkActivated(HyperlinkEvent e) {
				TaskListView taskListView = TaskListView.getFromActivePerspective();
				if (taskListView != null && taskListView.getDrilledIntoCategory() != null) {
					taskListView.goUpToRoot();
				}
				TasksUiInternal.refreshAndOpenTaskListElement((TasksUi.getTaskActivityManager().getActiveTask()));
			}
		});

		activeTaskLabel.addMouseListener(new MouseAdapter() {
			@Override
			public void mouseDown(MouseEvent e) {
				// only handle left clicks, context menu is handled by platform
				if (e.button == 1) {
					if (activeTask == null) {
						return;
					}

					TaskListView taskListView = TaskListView.getFromActivePerspective();
					if (taskListView != null && taskListView.getDrilledIntoCategory() != null) {
						taskListView.goUpToRoot();
					}

					TasksUiInternal.refreshAndOpenTaskListElement(activeTask);
				}
			}
		});

		return activeTaskLabel;
	}

	private void hookContextMenu() {
		activeTaskSelectionProvider = new SelectionProviderAdapter();

		actionGroup = new RepositoryElementActionGroup();
		actionGroup.setSelectionProvider(activeTaskSelectionProvider);

		menuManager = new MenuManager("#PopupMenu"); //$NON-NLS-1$
		menuManager.setRemoveAllWhenShown(true);
		menuManager.addMenuListener(new IMenuListener() {
			public void menuAboutToShow(IMenuManager manager) {
				actionGroup.fillContextMenu(manager);
				// trims do not have a workbench part so there is no simple way of registering the 
				// context menu
				ObjectActionContributorManager.getManager().contributeObjectActions(null, manager,
						activeTaskSelectionProvider);
			}
		});
	}

	public void indicateActiveTask() {
		if (activeTaskLabel != null && activeTaskLabel.isDisposed()) {
			return;
		}

		//activeTaskLabel.setText(shortenText(activeTask.getSummary()));
		activeTaskLabel.setText(activeTask.getSummary());
		activeTaskLabel.setUnderlined(true);
		activeTaskLabel.setToolTipText(activeTask.getSummary());
		activeTaskSelectionProvider.setSelection(new StructuredSelection(activeTask));
	}

	public void indicateNoActiveTask() {
		if (activeTaskLabel != null && activeTaskLabel.isDisposed()) {
			return;
		}

		activeTaskLabel.setText(Messages.TaskTrimWidget__no_active_task_);
		activeTaskLabel.setUnderlined(false);
		activeTaskLabel.setToolTipText(""); //$NON-NLS-1$
		activeTaskSelectionProvider.setSelection(StructuredSelection.EMPTY);
	}

//	// From PerspectiveBarContributionItem
//	private String shortenText(String taskLabel) {
//		if (taskLabel == null || composite == null || composite.isDisposed()) {
//			return null;
//		}
//
//		String returnText = taskLabel;
//		GC gc = new GC(composite);
//		int maxWidth = p.x;
//
//		if (gc.textExtent(taskLabel).x > maxWidth) {
//			for (int i = taskLabel.length(); i > 0; i--) {
//				String test = taskLabel.substring(0, i);
//				test = test + "...";
//				if (gc.textExtent(test).x < maxWidth) {
//					returnText = test;
//					break;
//				}
//			}
//		}
//
//		gc.dispose();
//		return returnText;
//	}
}
