/*******************************************************************************
 * 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.views;

import java.text.Collator;

import org.eclipse.core.runtime.Assert;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerSorter;
import org.eclipse.mylyn.internal.tasks.core.AbstractTask;
import org.eclipse.mylyn.internal.tasks.core.AbstractTaskContainer;
import org.eclipse.mylyn.internal.tasks.core.ITaskRepositoryElement;
import org.eclipse.mylyn.internal.tasks.core.RepositoryQuery;
import org.eclipse.mylyn.internal.tasks.core.ScheduledTaskContainer;
import org.eclipse.mylyn.internal.tasks.core.TaskCategory;
import org.eclipse.mylyn.internal.tasks.core.TaskGroup;
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.util.TaskComparator;
import org.eclipse.mylyn.tasks.core.ITask;
import org.eclipse.mylyn.tasks.core.TaskRepository;
import org.eclipse.mylyn.tasks.ui.TasksUi;
import org.eclipse.ui.IMemento;

/**
 * @author Mik Kersten
 */
public class TaskListSorter extends ViewerSorter {

	private static final String MEMENTO_KEY_SORTER = "sorter"; //$NON-NLS-1$

	private static final String MEMENTO_KEY_GROUP_BY = "groupBy"; //$NON-NLS-1$

	private static final TaskListSorter.GroupBy DEFAULT_GROUP_BY = TaskListSorter.GroupBy.NONE;

	public enum GroupBy {
		NONE, CATEGORY_QUERY, CATEGORY_REPOSITORY;

		public String getLabel() {
			switch (this) {
			case NONE:
				return Messages.TaskListSorter_No_Grouping;
			case CATEGORY_QUERY:
				return Messages.TaskListSorter_Catagory_and_Query;
			case CATEGORY_REPOSITORY:
				return Messages.TaskListSorter_Catagory_and_Repository;
			default:
				return null;
			}
		}

		public static GroupBy valueOfLabel(String label) {
			for (GroupBy value : values()) {
				if (value.getLabel().equals(label)) {
					return value;
				}
			}
			return null;
		}

	}

	private class SortElement {

		private int weight;

		private final String[] values = new String[3];

	}

	private final TaskComparator taskComparator;

	private GroupBy groupBy;

	private final SortElement key1;

	private final SortElement key2;

	public TaskListSorter() {
		this.taskComparator = new TaskComparator();
		this.groupBy = GroupBy.CATEGORY_QUERY;
		this.key1 = new SortElement();
		this.key2 = new SortElement();
	}

	/**
	 * compare - invoked when column is selected calls the actual comparison method for particular criteria
	 */
	@Override
	public int compare(Viewer compareViewer, Object o1, Object o2) {
		if (o1 instanceof AbstractTask && o2 instanceof AbstractTask) {
			// sort of the tasks within the container using the setting from the Sortdialog
			ITask element1 = (ITask) o1;
			ITask element2 = (ITask) o2;
			return compareElements(element1, element2);
		} else if (o1 instanceof ScheduledTaskContainer && o2 instanceof ScheduledTaskContainer) {
			// scheduled Mode compare
			ScheduledTaskContainer dateRangeTaskContainer1 = (ScheduledTaskContainer) o1;
			ScheduledTaskContainer dateRangeTaskContainer2 = (ScheduledTaskContainer) o2;
			return dateRangeTaskContainer1.getDateRange().compareTo(dateRangeTaskContainer2.getDateRange());
		} else {
			updateKey(key1, o1);
			updateKey(key2, o2);

			if (key1.weight != key2.weight) {
				return key1.weight - key2.weight < 0 ? -1 : 1;
			}
			int result = compare(key1.values[0], key2.values[0]);
			if (result != 0) {
				return result;
			}
			result = compare(key1.values[1], key2.values[1]);
			return (result != 0) ? result : compare(key1.values[2], key2.values[2]);
		}
	}

	private int compare(String key1, String key2) {
		if (key1 == null) {
			return (key2 != null) ? 1 : 0;
		} else if (key2 == null) {
			return -1;
		}
		return Collator.getInstance().compare(key1, key2);
	}

	private void updateKey(SortElement key, Object object) {
		int weight;
		if (object instanceof AbstractTask) {
			weight = 0;
		} else if (object instanceof UncategorizedTaskContainer) {
			weight = 1;
		} else if (object instanceof UnsubmittedTaskContainer) {
			weight = 2;
		} else if (object instanceof TaskCategory) {
			weight = 3;
		} else if (object instanceof RepositoryQuery) {
			weight = 4;
		} else if (object instanceof TaskGroup) { // support for the experimental grouping of tasks
			weight = 5;
		} else if (object instanceof UnmatchedTaskContainer) {
			weight = 6;
		} else {
			weight = 99;
		}

		key.values[0] = ((AbstractTaskContainer) object).getSummary();
		key.values[1] = null;
		key.values[2] = null;

		switch (groupBy) {
		case NONE:
			weight = 1;
			break;
		case CATEGORY_QUERY:
			break;
		case CATEGORY_REPOSITORY:
			if (weight == 1) {
				// keep
			} else if (weight == 3) {
				weight = 2;
			} else {
				key.values[0] = getRepositoryUrl(object);
				key.values[1] = weight + ""; //$NON-NLS-1$
				key.values[2] = ((AbstractTaskContainer) object).getSummary();
				weight = 3;
			}
			break;
		}

		key.weight = weight;
	}

	private String getRepositoryUrl(Object object) {
		if (object instanceof ITaskRepositoryElement) {
			ITaskRepositoryElement repositoryElement = (ITaskRepositoryElement) object;
			String repositoryUrl = repositoryElement.getRepositoryUrl();
			TaskRepository taskRepository = TasksUi.getRepositoryManager().getRepository(
					repositoryElement.getConnectorKind(), repositoryUrl);
			return taskRepository != null ? taskRepository.getRepositoryLabel() : null;
		}
//		if (object instanceof UnsubmittedTaskContainer) {
//			return ((UnsubmittedTaskContainer) object).getRepositoryUrl();
//		} else if (object instanceof RepositoryQuery) {
//			return ((RepositoryQuery) object).getRepositoryUrl();
//		} else if (object instanceof UnmatchedTaskContainer) {
//			return ((UnmatchedTaskContainer) object).getRepositoryUrl();
//		}
		return null;
	}

	private int compareElements(ITask element1, ITask element2) {
		return taskComparator.compare(element1, element2);
	}

	@Override
	public TaskComparator getComparator() {
		return taskComparator;
	}

	public GroupBy getGroupBy() {
		return groupBy;
	}

	public void setGroupBy(GroupBy sortByIndex) {
		Assert.isNotNull(sortByIndex);
		this.groupBy = sortByIndex;
	}

	public void restoreState(IMemento memento) {
		IMemento child = memento.getChild(MEMENTO_KEY_SORTER);
		if (child != null) {
			getComparator().restoreState(child);
		}
		setGroupBy(getGroupBy(memento, MEMENTO_KEY_GROUP_BY, DEFAULT_GROUP_BY));
	}

	public void saveState(IMemento memento) {
		IMemento child = memento.createChild(MEMENTO_KEY_SORTER);
		if (child != null) {
			getComparator().saveState(child);
		}
		memento.putString(MEMENTO_KEY_GROUP_BY, getGroupBy().name());
	}

	private TaskListSorter.GroupBy getGroupBy(IMemento memento, String key, TaskListSorter.GroupBy defaultValue) {
		String value = memento.getString(key);
		if (value != null) {
			try {
				return TaskListSorter.GroupBy.valueOf(value);
			} catch (IllegalArgumentException e) {
				// ignore
			}
		}
		return defaultValue;
	}
}
