/*******************************************************************************
 * 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
 *     Frank Becker - improvements
 *     Perforce - enhancements for bug 319469
 *******************************************************************************/

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

import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IContributionManager;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.util.LocalSelectionTransfer;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.StructuredViewer;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerFilter;
import org.eclipse.mylyn.commons.ui.CommonImages;
import org.eclipse.mylyn.commons.workbench.DecoratingPatternStyledCellLabelProvider;
import org.eclipse.mylyn.commons.workbench.EnhancedFilteredTree;
import org.eclipse.mylyn.commons.workbench.SubstringPatternFilter;
import org.eclipse.mylyn.internal.tasks.core.AbstractTask;
import org.eclipse.mylyn.internal.tasks.core.AbstractTaskCategory;
import org.eclipse.mylyn.internal.tasks.core.TaskGroup;
import org.eclipse.mylyn.internal.tasks.core.UnmatchedTaskContainer;
import org.eclipse.mylyn.internal.tasks.ui.actions.OpenTaskSearchAction;
import org.eclipse.mylyn.internal.tasks.ui.actions.OpenWithBrowserAction;
import org.eclipse.mylyn.internal.tasks.ui.search.SearchResultTreeContentProvider.GroupBy;
import org.eclipse.mylyn.internal.tasks.ui.util.TaskDragSourceListener;
import org.eclipse.mylyn.internal.tasks.ui.util.TasksUiInternal;
import org.eclipse.mylyn.internal.tasks.ui.views.TaskListToolTip;
import org.eclipse.mylyn.internal.tasks.ui.views.TaskListView;
import org.eclipse.mylyn.tasks.core.IRepositoryQuery;
import org.eclipse.mylyn.tasks.core.ITask;
import org.eclipse.mylyn.tasks.ui.ITasksUiConstants;
import org.eclipse.mylyn.tasks.ui.TasksUiImages;
import org.eclipse.search.ui.IContextMenuConstants;
import org.eclipse.search.ui.ISearchQuery;
import org.eclipse.search.ui.ISearchResult;
import org.eclipse.search.ui.text.AbstractTextSearchViewPage;
import org.eclipse.search.ui.text.Match;
import org.eclipse.swt.SWT;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.FileTransfer;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Layout;
import org.eclipse.ui.IMemento;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.dialogs.FilteredTree;
import org.eclipse.ui.part.IShowInTargetList;

/**
 * Displays the results of a Repository search.
 * 
 * @see org.eclipse.search.ui.text.AbstractTextSearchViewPage
 * @author Rob Elves
 * @author Mik Kersten
 * @author Shawn Minto
 * @author Frank Becker
 * @author Steffen Pingel
 */
public class RepositorySearchResultView extends AbstractTextSearchViewPage implements IAdaptable {

	private class GroupingAction extends Action {

		private final GroupBy groupBy;

		public GroupingAction(String text, GroupBy groupBy) {
			super(text, IAction.AS_CHECK_BOX);
			this.groupBy = groupBy;
			groupingActions.add(this);
		}

		@Override
		public void run() {
			for (GroupingAction action : groupingActions) {
				action.setChecked(false);
			}

			SearchResultTreeContentProvider contentProvider = (SearchResultTreeContentProvider) getViewer().getContentProvider();
			if (contentProvider.getSelectedGroup() == groupBy) {
				contentProvider.setSelectedGroup(GroupBy.NONE);
			} else {
				contentProvider.setSelectedGroup(groupBy);
				setChecked(true);
			}
			getViewer().refresh();
		}
	}

	private class FilteringAction extends Action {

		private final ViewerFilter filter;

		public FilteringAction(String text, ViewerFilter filter) {
			super(text, IAction.AS_CHECK_BOX);
			this.filter = filter;
			filterActions.add(this);
		}

		@Override
		public void runWithEvent(Event event) {
			if (isChecked()) {
				getViewer().addFilter(filter);
			} else {
				getViewer().removeFilter(filter);
			}
		}
	}

	private static final String MEMENTO_KEY_SORT = "sort"; //$NON-NLS-1$

	private SearchResultContentProvider searchResultProvider;

	private final OpenSearchResultAction openInEditorAction;

	private final CreateQueryFromSearchAction createQueryAction;

	private final Action refineSearchAction;

	private static final String[] SHOW_IN_TARGETS = new String[] { ITasksUiConstants.ID_VIEW_TASKS };

	private TaskListToolTip toolTip;

	private final List<GroupingAction> groupingActions;

	private final List<FilteringAction> filterActions;

	private final OpenWithBrowserAction openSearchWithBrowserAction;

	private final SearchResultSorter searchResultSorter;

	private SearchResultSortAction sortByDialogAction;

	private DecoratingPatternStyledCellLabelProvider styledLabelProvider;

	private static final IShowInTargetList SHOW_IN_TARGET_LIST = new IShowInTargetList() {
		public String[] getShowInTargetIds() {
			return SHOW_IN_TARGETS;
		}
	};

	public RepositorySearchResultView() {
		// Only use the table layout.
		super(FLAG_LAYOUT_TREE);

		openInEditorAction = new OpenSearchResultAction(Messages.RepositorySearchResultView_Open_in_Editor, this);
		createQueryAction = new CreateQueryFromSearchAction(
				Messages.RepositorySearchResultView_Create_Query_from_Search_, this);
		refineSearchAction = new OpenTaskSearchAction();
		refineSearchAction.setText(Messages.RepositorySearchResultView_Refine_Search_);
		openSearchWithBrowserAction = new OpenWithBrowserAction();
		openSearchWithBrowserAction.setText(Messages.RepositorySearchResultView_Open_Search_with_Browser_Label);

		groupingActions = new ArrayList<GroupingAction>();
		GroupingAction groupByOwnerAction = new GroupingAction(Messages.RepositorySearchResultView_Group_By_Owner,
				GroupBy.OWNER);
		groupByOwnerAction.setImageDescriptor(CommonImages.PRESENTATION);
//		new GroupingAction(Messages.RepositorySearchResultView_Group_By_Complete, GroupBy.COMPLETION);

		filterActions = new ArrayList<FilteringAction>();
		FilteringAction filterCompleteAction = new FilteringAction(
				Messages.RepositorySearchResultView_Filter_Completed_Tasks, new ViewerFilter() {
					@Override
					public boolean select(Viewer viewer, Object parentElement, Object element) {
						if (element instanceof ITask) {
							return !((ITask) element).isCompleted();
						} else if (element instanceof TaskGroup) {
							TaskGroup taskGroup = (TaskGroup) element;
							return taskGroup.getHandleIdentifier().equals("group-incompleteIncomplete"); //$NON-NLS-1$
						}
						return true;
					}
				});
		filterCompleteAction.setImageDescriptor(CommonImages.FILTER_COMPLETE);

		// construct early since to be ready when restoreState() is invoked
		searchResultSorter = new SearchResultSorter();
	}

	@Override
	protected void elementsChanged(Object[] objects) {
		if (searchResultProvider != null) {
			searchResultProvider.elementsChanged(objects);
			getViewer().refresh();
		}
	}

	@Override
	protected void clear() {
		if (searchResultProvider != null) {
			searchResultProvider.clear();
			getViewer().refresh();
		}
	}

	// Allows the inherited method "getViewer" to be accessed publicly.
	@Override
	public StructuredViewer getViewer() {
		return super.getViewer();
	}

	@Override
	protected void configureTreeViewer(TreeViewer viewer) {
		viewer.setUseHashlookup(true);
		searchResultProvider = new SearchResultTreeContentProvider();
		viewer.setContentProvider(searchResultProvider);

		styledLabelProvider = new DecoratingPatternStyledCellLabelProvider(new SearchResultsLabelProvider(
				searchResultProvider, viewer), PlatformUI.getWorkbench().getDecoratorManager().getLabelDecorator(),
				null);
		viewer.setLabelProvider(styledLabelProvider);
		viewer.setSorter(searchResultSorter);

		Transfer[] dragTypes = new Transfer[] { LocalSelectionTransfer.getTransfer(), FileTransfer.getInstance() };

		getViewer().addDragSupport(DND.DROP_COPY | DND.DROP_MOVE | DND.DROP_LINK, dragTypes,
				new TaskDragSourceListener(getViewer()));

		sortByDialogAction = new SearchResultSortAction(this);

		toolTip = new TaskListToolTip(viewer.getControl());
	}

	@Override
	protected TreeViewer createTreeViewer(Composite parent) {
		// create a filtered tree
		Composite treeComposite = parent;
		Layout parentLayout = parent.getLayout();
		if (!(parentLayout instanceof GridLayout)) {
			treeComposite = new Composite(parent, SWT.NONE);
			GridLayout layout = new GridLayout();
			layout.marginHeight = 0;
			layout.marginWidth = 0;
			treeComposite.setLayout(layout);
		}

		FilteredTree searchTree = new EnhancedFilteredTree(treeComposite, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL,
				new SubstringPatternFilter() {

					@Override
					public void setPattern(String patternString) {
						styledLabelProvider.setPattern(patternString);
						super.setPattern(patternString);
					}

				}, true);
		return searchTree.getViewer();
	}

	@Override
	protected void configureTableViewer(TableViewer viewer) {
//		viewer.setUseHashlookup(true);
//		String[] columnNames = new String[] { "Summary" };
//		TableColumn[] columns = new TableColumn[columnNames.length];
//		int[] columnWidths = new int[] { 500 };
//		viewer.setColumnProperties(columnNames);
//
//		viewer.getTable().setHeaderVisible(false);
//		for (int i = 0; i < columnNames.length; i++) {
//			columns[i] = new TableColumn(viewer.getTable(), 0, i); // SWT.LEFT
//			columns[i].setText(columnNames[i]);
//			columns[i].setWidth(columnWidths[i]);
//			columns[i].setData(new Integer(i));
//			columns[i].addSelectionListener(new SelectionAdapter() {
//
//				@Override
//				public void widgetSelected(SelectionEvent e) {
//					TableColumn col = (TableColumn) e.getSource();
//					Integer integer = (Integer) col.getData();
//					setSortOrder(integer.intValue());
//				}
//			});
//		}
//
//		IThemeManager themeManager = getSite().getWorkbenchWindow().getWorkbench().getThemeManager();
//		Color categoryBackground = themeManager.getCurrentTheme().getColorRegistry().get(
//				TaskListColorsAndFonts.THEME_COLOR_TASKLIST_CATEGORY);
//
//		SearchViewTableLabelProvider taskListTableLabelProvider = new SearchViewTableLabelProvider(
//				new TaskElementLabelProvider(true),
//				PlatformUI.getWorkbench().getDecoratorManager().getLabelDecorator(), categoryBackground);
//
//		viewer.setLabelProvider(taskListTableLabelProvider);
//		viewer.setContentProvider(new SearchResultTableContentProvider(this));
//
//		// Set the order when the search view is loading so that the items are
//		// sorted right away
//		setSortOrder(currentSortOrder);
//
//		taskContentProvider = (SearchResultContentProvider) viewer.getContentProvider();
	}

	@Override
	public void dispose() {
		toolTip.dispose();
		super.dispose();
	}

	@SuppressWarnings("rawtypes")
	public Object getAdapter(Class adapter) {
		return getAdapterDelegate(adapter);
	}

	private Object getAdapterDelegate(Class<?> adapter) {
		if (IShowInTargetList.class.equals(adapter)) {
			return SHOW_IN_TARGET_LIST;
		}
		return null;
	}

	@Override
	protected void showMatch(Match match, int currentOffset, int currentLength, boolean activate)
			throws PartInitException {
		AbstractTask repositoryHit = (AbstractTask) match.getElement();
		TasksUiInternal.refreshAndOpenTaskListElement(repositoryHit);
	}

	@Override
	protected void fillContextMenu(IMenuManager menuManager) {
		super.fillContextMenu(menuManager);

		// open actions
		menuManager.appendToGroup(IContextMenuConstants.GROUP_OPEN, openInEditorAction);

		// Add to Task List menu
		// HACK: this should be a contribution
		final MenuManager subMenuManager = new MenuManager(MessageFormat.format(
				Messages.RepositorySearchResultView_Add_to_X_Category, TaskListView.LABEL_VIEW));
		List<AbstractTaskCategory> categories = new ArrayList<AbstractTaskCategory>(TasksUiInternal.getTaskList()
				.getCategories());
		Collections.sort(categories);
		for (final AbstractTaskCategory category : categories) {
			if (!(category instanceof UnmatchedTaskContainer)) {//.equals(TasksUiPlugin.getTaskList().getArchiveContainer())) {
				Action action = new Action() {
					@Override
					public void run() {
						moveToCategory(category);
					}
				};
				String text = category.getSummary();
				action.setText(text);
				action.setImageDescriptor(TasksUiImages.CATEGORY);
				subMenuManager.add(action);
			}
		}
		menuManager.appendToGroup(IContextMenuConstants.GROUP_OPEN, subMenuManager);

		// search actions

		menuManager.appendToGroup(IContextMenuConstants.GROUP_SEARCH, createQueryAction);
		menuManager.appendToGroup(IContextMenuConstants.GROUP_SEARCH, refineSearchAction);
		menuManager.appendToGroup(IContextMenuConstants.GROUP_SEARCH, openSearchWithBrowserAction);

		menuManager.appendToGroup(IContextMenuConstants.GROUP_VIEWER_SETUP, sortByDialogAction);
		addPresentationActions(menuManager);
	}

	private void moveToCategory(AbstractTaskCategory category) {
		StructuredSelection selection = (StructuredSelection) this.getViewer().getSelection();
		for (Iterator<?> iterator = selection.iterator(); iterator.hasNext();) {
			Object selectedObject = iterator.next();
			if (selectedObject instanceof ITask) {
				ITask task = (ITask) selectedObject;
				TasksUiInternal.getTaskList().addTask(task, category);
			}
		}
	}

	@Override
	public void createControl(Composite parent) {
		super.createControl(parent);
		IMenuManager menuManager = getSite().getActionBars().getMenuManager();
		menuManager.appendToGroup(IContextMenuConstants.GROUP_VIEWER_SETUP, sortByDialogAction);
		addPresentationActions(menuManager);

		addPresentationActions(getSite().getActionBars().getToolBarManager());
	}

	public void addPresentationActions(IContributionManager menuManager) {
		for (Action action : groupingActions) {
			menuManager.appendToGroup(IContextMenuConstants.GROUP_VIEWER_SETUP, action);
		}
		menuManager.appendToGroup(IContextMenuConstants.GROUP_VIEWER_SETUP, new Separator());
		for (Action action : filterActions) {
			menuManager.appendToGroup(IContextMenuConstants.GROUP_VIEWER_SETUP, action);
		}
	}

	@Override
	public void setInput(ISearchResult newSearch, Object viewState) {
		super.setInput(newSearch, viewState);
		if (newSearch != null) {
			ISearchQuery query = ((RepositorySearchResult) newSearch).getQuery();
			IRepositoryQuery repositoryQuery = ((SearchHitCollector) query).getRepositoryQuery();
			openSearchWithBrowserAction.selectionChanged(new StructuredSelection(repositoryQuery));
		} else {
			openSearchWithBrowserAction.selectionChanged(StructuredSelection.EMPTY);
		}
	}

	public SearchResultSorter getSorter() {
		return searchResultSorter;
	}

	@Override
	public void restoreState(IMemento memento) {
		super.restoreState(memento);
		if (memento != null) {
			IMemento child = memento.getChild(MEMENTO_KEY_SORT);
			if (child != null && searchResultSorter != null) {
				searchResultSorter.getTaskComparator().restoreState(child);
			}
		}
	}

	@Override
	public void saveState(IMemento memento) {
		super.saveState(memento);
		if (memento != null) {
			IMemento child = memento.createChild(MEMENTO_KEY_SORT);
			if (searchResultSorter != null) {
				searchResultSorter.getTaskComparator().saveState(child);
			}
		}
	}

}
