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

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

import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.mylyn.commons.core.StatusHandler;
import org.eclipse.mylyn.context.core.ContextCore;
import org.eclipse.mylyn.context.core.IInteractionContext;
import org.eclipse.mylyn.context.core.IInteractionElement;
import org.eclipse.mylyn.tasks.core.ITask;
import org.eclipse.mylyn.tasks.ui.TasksUi;
import org.eclipse.mylyn.team.ui.AbstractActiveChangeSetProvider;
import org.eclipse.mylyn.team.ui.AbstractContextChangeSetManager;
import org.eclipse.mylyn.team.ui.IContextChangeSet;
import org.eclipse.team.internal.core.subscribers.ActiveChangeSet;
import org.eclipse.team.internal.core.subscribers.ActiveChangeSetManager;
import org.eclipse.team.internal.core.subscribers.ChangeSet;
import org.eclipse.team.internal.core.subscribers.IChangeSetChangeListener;

/**
 * NOTE: this class contains several work-arounds for change set limitations in the Platform/Team support.
 * 
 * @author Mik Kersten
 */
public class ContextActiveChangeSetManager extends AbstractContextChangeSetManager {

	private final List<ActiveChangeSetManager> changeSetManagers = new ArrayList<ActiveChangeSetManager>();

	private final List<IContextChangeSet> activeChangeSets = new ArrayList<IContextChangeSet>();

	private final Map<ActiveChangeSetManager, ActiveChangeSet> noTaskSetMap = new HashMap<ActiveChangeSetManager, ActiveChangeSet>();;

	private final Map<ActiveChangeSetManager, ChangeSetChangeListener> listenerByManager = new HashMap<ActiveChangeSetManager, ChangeSetChangeListener>();

	/**
	 * Used to restore change sets managed with task context when platform deletes them, bug 168129
	 */
	private class ChangeSetChangeListener implements IChangeSetChangeListener {

		private final ActiveChangeSetManager manager;

		public ChangeSetChangeListener(ActiveChangeSetManager manager) {
			this.manager = manager;
		}

		public void setRemoved(ChangeSet set) {
			if (set instanceof IContextChangeSet) {
				IContextChangeSet contextChangeSet = (IContextChangeSet) set;
				// never matches the noTask change set: its task is never active
				if (contextChangeSet.getTask() != null && contextChangeSet.getTask().isActive()) {
					// put it back
					manager.add((ActiveChangeSet) contextChangeSet);
				}
			}
		}

		public void setAdded(ChangeSet set) {
			// ignore
		}

		public void defaultSetChanged(ChangeSet previousDefault, ChangeSet set) {
			// ignore
		}

		public void nameChanged(ChangeSet set) {
			// ignore
		}

		public void resourcesChanged(ChangeSet set, IPath[] paths) {
			// ignore
		}

	};

	public ContextActiveChangeSetManager() {
		Collection<AbstractActiveChangeSetProvider> providerList = FocusedTeamUiPlugin.getDefault()
				.getActiveChangeSetProviders();
		for (AbstractActiveChangeSetProvider provider : providerList) {
			ActiveChangeSetManager changeSetManager = provider.getActiveChangeSetManager();
			if (changeSetManager != null) {
				changeSetManagers.add(changeSetManager);
			}
		}
	}

	@Override
	protected void updateChangeSetLabel(ITask task) {
		for (ActiveChangeSetManager collector : changeSetManagers) {
			ChangeSet[] sets = collector.getSets();
			for (ChangeSet set : sets) {
				if (set instanceof IContextChangeSet) {
					IContextChangeSet contextChangeSet = (IContextChangeSet) set;
					if (contextChangeSet.getTask().equals(task)) {
						contextChangeSet.updateLabel();
					}
				}
			}
		}
	}

	@Override
	public void enable() {
		super.enable();
		for (ActiveChangeSetManager collector : changeSetManagers) {
			ChangeSetChangeListener listener = listenerByManager.get(collector);
			if (listener == null) {
				listener = new ChangeSetChangeListener(collector);
				listenerByManager.put(collector, listener);
				collector.addListener(listener);
			}
		}
	}

	@Override
	public void disable() {
		super.disable();
		for (ActiveChangeSetManager collector : changeSetManagers) {
			ChangeSetChangeListener listener = listenerByManager.get(collector);
			if (listener != null) {
				collector.removeListener(listener);
				listenerByManager.remove(collector);
			}
		}
	}

	@Override
	protected void initContextChangeSets() {
		// replace existing change sets with IContextChangeSet
		for (ActiveChangeSetManager manager : changeSetManagers) {
			ChangeSet[] sets = manager.getSets();
			for (ChangeSet restoredSet : sets) {
				if (!(restoredSet instanceof IContextChangeSet)) {
					String encodedTitle = restoredSet.getName();
					String taskHandle = ContextChangeSet.getHandleFromPersistedTitle(encodedTitle);
					ITask task = TasksUi.getRepositoryModel().getTask(taskHandle);
					if (task != null) {
						try {
							IContextChangeSet contextChangeSet = getOrCreateSet(manager, task);
							if (contextChangeSet instanceof ActiveChangeSet) {
								contextChangeSet.restoreResources(restoredSet.getResources());
								manager.remove(restoredSet);
								manager.add((ActiveChangeSet) contextChangeSet);
							}
						} catch (Exception e) {
							StatusHandler.log(new Status(IStatus.ERROR, FocusedTeamUiPlugin.ID_PLUGIN,
									"Could not restore change set", e)); //$NON-NLS-1$
						}
					}
				}
			}
		}
	}

	/**
	 * For testing.
	 */
	public void clearActiveChangeSets() {
		activeChangeSets.clear();
	}

	public IResource[] getResources(ITask task) {
		for (ActiveChangeSetManager collector : changeSetManagers) {
			ChangeSet[] sets = collector.getSets();
			for (ChangeSet set : sets) {
				if (set instanceof IContextChangeSet) {
					IContextChangeSet contextChangeSet = (IContextChangeSet) set;
					if (contextChangeSet.getTask().equals(task) && contextChangeSet instanceof ActiveChangeSet) {
						return ((ActiveChangeSet) contextChangeSet).getResources();
					}
				}
			}
		}
		return null;
	}

	// TODO m4.0 remove deprecated method that is needed maintain backwards compatibility
	@SuppressWarnings({ "restriction", "deprecation" })
	@Override
	public void contextActivated(IInteractionContext context) {
		try {
			ITask task = getTask(context);
			if (task != null) {
				for (ActiveChangeSetManager manager : changeSetManagers) {
					IContextChangeSet contextChangeSet = getOrCreateSet(manager, task);
					if (contextChangeSet instanceof ActiveChangeSet) {
						ActiveChangeSet activeChangeSet = (ActiveChangeSet) contextChangeSet;
//						List<IResource> interestingResources = ResourcesUiBridgePlugin.getDefault()
//								.getInterestingResources(context);
//						activeChangeSet.add(interestingResources.toArray(new IResource[interestingResources.size()]));
						activeChangeSets.add(contextChangeSet);

						// makeDefault() will add the change set 
//						if (!manager.contains(activeChangeSet)) {
//							manager.add(activeChangeSet);
//						}
						manager.makeDefault(activeChangeSet);
					}
				}
			}
		} catch (Exception e) {
			StatusHandler.log(new Status(IStatus.ERROR, FocusedTeamUiPlugin.ID_PLUGIN, "Could not update change set", e)); //$NON-NLS-1$
		}
	}

	private IContextChangeSet getOrCreateSet(ActiveChangeSetManager manager, ITask task) {
		ChangeSet[] sets = manager.getSets();
		for (ChangeSet set : sets) {
			if (set instanceof IContextChangeSet && task.equals(((IContextChangeSet) set).getTask())) {
				return (IContextChangeSet) set;
			}
		}
		// change set does not exist, create a new one
		AbstractActiveChangeSetProvider provider = FocusedTeamUiPlugin.getDefault().getActiveChangeSetProvider(manager);
		return provider.createChangeSet(task);
	}

	// TODO m4.0 remove deprecated method that is needed maintain backwards compatibility
	@SuppressWarnings("deprecation")
	@Override
	public void contextDeactivated(IInteractionContext context) {
		for (ActiveChangeSetManager collector : changeSetManagers) {
			ChangeSet[] sets = collector.getSets();
			for (ChangeSet set : sets) {
				if (set instanceof ActiveChangeSet) {
					IResource[] resources = set.getResources();
					if (resources == null || resources.length == 0) {
						collector.remove(set);
					}
				}
			}

			AbstractActiveChangeSetProvider changeSetProvider = FocusedTeamUiPlugin.getDefault()
					.getActiveChangeSetProvider(collector);

			changeSetProvider.activateDefaultChangeSet();
		}
		activeChangeSets.clear();
	}

	// TODO m4.0 remove deprecated method that is needed maintain backwards compatibility
	@SuppressWarnings({ "restriction", "deprecation" })
	@Override
	public void interestChanged(List<IInteractionElement> elements) {
		// disabled, see bug 162007
		//		for (IInteractionElement element : elements) {
//			AbstractContextStructureBridge bridge = ContextCore.getStructureBridge(element.getContentType());
//			try {
//				if (bridge.isDocument(element.getHandleIdentifier())) {
//					IResource resource = ResourcesUiBridgePlugin.getDefault().getResourceForElement(element, false);
//					if (resource != null && resource.exists()) {
//						for (IContextChangeSet activeContextChangeSet : getActiveChangeSets()) {
//							if (activeContextChangeSet instanceof ActiveChangeSet) {
//								if (!((ActiveChangeSet) activeContextChangeSet).contains(resource)) {
//									if (element.getInterest().isInteresting()) {
//										((ActiveChangeSet) activeContextChangeSet).add(new IResource[] { resource });
//									}
//								}
//							}
//						}
//						if (shouldRemove(element)) {
//							for (ActiveChangeSetManager collector : changeSetManagers) {
//								ChangeSet[] sets = collector.getSets();
//								for (ChangeSet set : sets) {
//									if (set instanceof ActiveChangeSet) {
//										set.remove(resource);
//									}
//								}
//							}
//						}
//					}
//				}
//			} catch (Exception e) {
//				StatusHandler.log(new Status(IStatus.ERROR, FocusedTeamUiPlugin.ID_PLUGIN,
//						"Could not manipulate change set resources", e)); //$NON-NLS-1$
//			}
//		}
	}

	public List<IContextChangeSet> getActiveChangeSets() {
		return new ArrayList<IContextChangeSet>(activeChangeSets);
	}

	private ITask getTask(IInteractionContext context) {
		return TasksUi.getTaskActivityManager().getActiveTask();
	}

	/**
	 * Ignores decay.
	 */
	private boolean shouldRemove(IInteractionElement element) {
		// TODO: generalize this logic?
		return (element.getInterest().getValue() + element.getInterest().getDecayValue()) < ContextCore.getCommonContextScaling()
				.getInteresting();
	}
}
