/*******************************************************************************
 * Copyright (c) 2009, 2010 IBM Corporation 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:
 * IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.team.internal.ccvs.core;

import java.util.*;

import org.eclipse.core.resources.IProject;
import org.eclipse.team.internal.ccvs.core.CVSProjectSetCapability.LoadInfo;
import org.eclipse.team.internal.ccvs.core.util.KnownRepositories;

public class CVSRepositoryLocationMatcher {

	private static final String EXTSSH = "extssh"; //$NON-NLS-1$
	private static final String PSERVER = "pserver"; //$NON-NLS-1$
	private static final String EXT = "ext"; //$NON-NLS-1$

	private static Comparator COMPATIBLE_LOCATIONS_COMPARATOR = new Comparator() {
		public int compare(Object o1, Object o2) {
			if (o1 instanceof ICVSRepositoryLocation
					&& o2 instanceof ICVSRepositoryLocation) {
				ICVSRepositoryLocation rl1 = (ICVSRepositoryLocation) o1;
				ICVSRepositoryLocation rl2 = (ICVSRepositoryLocation) o2;
				String name1 = rl1.getMethod().getName();
				String name2 = rl2.getMethod().getName();

				if (!name1.equals(name2) && isCompatible(rl1, rl2, false)) {
					if (name1.equals(EXTSSH))
						return -1;
					if (name2.equals(EXTSSH))
						return 1;
					if (name1.equals(PSERVER))
						return -1;
					if (name2.equals(PSERVER))
						return 1;
					if (name1.equals(EXT))
						return -1;
					if (name2.equals(EXT))
						return 1;
				}
				return name1.compareTo(name2);
			}
			return 0;
		}
	};

	public static Map/* <IProject, List<ICVSRepositoryLocation>> */prepareSuggestedRepositoryLocations(
			IProject[] projects, final Map/* <IProject, LoadInfo> */infoMap) {
		List/* <IProject> */confirmedProjectsList = Arrays.asList(projects);
		Set/* <ICVSRepositoryLocation> */projectSetRepositoryLocations = new HashSet();
		for (Iterator i = infoMap.keySet().iterator(); i.hasNext();) {
			IProject project = (IProject) i.next();
			if (confirmedProjectsList.contains(project)) {
				LoadInfo loadInfo = (LoadInfo) infoMap.get(project);
				projectSetRepositoryLocations.add(loadInfo.repositoryLocation);
			}
		}

		// none of projects from project sets is confirmed to overwrite
		if (projectSetRepositoryLocations.isEmpty()) {
			return null;
		}

		List/* <ICVSRepositoryLocation> */knownRepositories = Arrays
				.asList(KnownRepositories.getInstance().getRepositories());

		if (knownRepositories.isEmpty()) {
			// There are no known repositories so use repository location from
			// the project set.
			Map result = new HashMap();
			for (Iterator i = projectSetRepositoryLocations.iterator(); i
					.hasNext();) {
				ICVSRepositoryLocation projectSetRepositoryLocation = (ICVSRepositoryLocation) i
						.next();
				ArrayList list = new ArrayList(1);
				list.add(projectSetRepositoryLocation);
				result.put(projectSetRepositoryLocation, list);
			}
			return result;
		} else if (knownRepositories.containsAll(projectSetRepositoryLocations)) {
			// All repositories are known, no need to prompt for additional
			// information.
			return Collections.EMPTY_MAP;
		} else {
			// Not all repositories from the project set are known.
			Map result = new HashMap();

			for (Iterator i = projectSetRepositoryLocations.iterator(); i
					.hasNext();) {
				ICVSRepositoryLocation projectSetRepositoryLocation = (ICVSRepositoryLocation) i
						.next();

				List matching = new ArrayList();
				List compatible = new ArrayList();
				List list = new ArrayList();
				for (Iterator j = knownRepositories.iterator(); j.hasNext();) {
					ICVSRepositoryLocation knownRepositoryLocation = (ICVSRepositoryLocation) j
							.next();
					// There can be more than one perfect match (i.e. two
					// known, matching repositories with different user names)
					if (CVSRepositoryLocationMatcher.isMatching(
							projectSetRepositoryLocation,
							knownRepositoryLocation)) {
						matching.add(knownRepositoryLocation);
					} else if (CVSRepositoryLocationMatcher.isCompatible(
							knownRepositoryLocation,
							projectSetRepositoryLocation, false)) {
						compatible.add(knownRepositoryLocation);
					} else {
						list.add(knownRepositoryLocation);
					}
				}

				// Sort compatible repository locations starting from extssh,
				// followed by pserver and finally ext.
				Collections.sort(compatible, COMPATIBLE_LOCATIONS_COMPARATOR);

				// Add compatible repos before others
				list.addAll(0, compatible);

				if (matching.isEmpty()) {
					// If no matching repo locations found add the one
					// from the project set first.
					list.add(0, projectSetRepositoryLocation);
				} else if (matching.size() == 1) {
					// There is only one matching, known repository
					// so there is no need to ask for any additional info.
					// Don't add it to the 'resultMap'
					list.clear();
					list.addAll(matching);
					result.put(projectSetRepositoryLocation, list);
					continue;
				} else {
					// There is more than one matching, known repository, so
					// ask which one we should use during the import
					list.addAll(0, matching);
				}

				result.put(projectSetRepositoryLocation, list);
			}
			return result;
		}
	}

	/**
	 * Same check as in
	 * org.eclipse.team.internal.ccvs.ui.CVSProjectPropertiesPage class.
	 * 
	 * @see org.eclipse.team.internal.ccvs.ui.CVSProjectPropertiesPage#isCompatible
	 * 
	 * @param location1
	 *            First repository location to match
	 * @param location2
	 *            Second repository location to match
	 * @param equalIsCompatible
	 *            If equal means compatible
	 * @return <code>true</code> if given repository location are compatible,
	 *         otherwise <code>false</code> is returned.
	 */
	public static boolean isCompatible(ICVSRepositoryLocation location1,
			ICVSRepositoryLocation location2, boolean equalIsCompatible) {
		if (!location1.getHost().equals(location2.getHost()))
			return false;
		if (!location1.getRootDirectory().equals(location2.getRootDirectory()))
			return false;
		if (!equalIsCompatible && location1.equals(location2))
			return false;
		return true;
	}

	/**
	 * Checks whether two repository locations match (i.e. they are compatible,
	 * they use the same connection method and they use the same port) .
	 * 
	 * @param location1
	 *            First repository location to match
	 * @param location2
	 *            Second repository location to match
	 * @return <code>true</code> if given repository location are matching
	 *         according to the rule above, otherwise <code>false</code> is
	 *         returned.
	 */
	public static boolean isMatching(ICVSRepositoryLocation location1,
			ICVSRepositoryLocation location2) {
		if (isCompatible(location1, location2, true)
				&& location2.getMethod() == location1.getMethod()
				&& location2.getPort() == location1.getPort())
			return true;
		return false;
	}

	static boolean isPromptRequired(Map suggestedRepositoryLocations) {
		if (suggestedRepositoryLocations == null)
			return false;
		for (Iterator i = suggestedRepositoryLocations.values().iterator(); i
				.hasNext();) {
			List list = (List) i.next();
			if (!list.isEmpty())
				return true;
		}
		return false;
	}

}