/*******************************************************************************
 * Copyright (c) 2000, 2006 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.ui.actions;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;

import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.IPath;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.team.internal.ccvs.core.ICVSResource;
import org.eclipse.team.internal.ccvs.core.resources.CVSWorkspaceRoot;
import org.eclipse.team.internal.ui.Utils;

/**
 * This class represents a selection for a set of CVS actions.
 */
public class CVSActionSelectionProperties {
    
    // Use a weak hash map so that the properties ae collected when the selection is no longer referenced
    private static Map selectionProperties = new WeakHashMap();
    
    private Object[] selection;
    private Map properties = new HashMap();
    
    private static final String SELECTED_RESOURCES = "selectedResources"; //$NON-NLS-1$
    private static final String NONOVERLAPPING_SELECTED_RESOURCES = "nonoverlappingSelectedResources"; //$NON-NLS-1$
    private static final String CVS_RESOURCE_MAP = "cvsResourceMap"; //$NON-NLS-1$
    
    public static CVSActionSelectionProperties getProperties(IStructuredSelection selection) {
        if (selection == null) return null;
        CVSActionSelectionProperties props = (CVSActionSelectionProperties)selectionProperties.get(selection);
        if (props == null) {
            props = new CVSActionSelectionProperties(selection);
            selectionProperties.put(selection, props);
        }
        return props;
    }
    
    public CVSActionSelectionProperties(IStructuredSelection selection) {
        // Copy the selection so that the WeakHashMap will not think the seleciton used for the key is still in use
        this.selection = selection.toArray();
    }

    public void put(String key, Object value) {
        properties.put(key, value);
    }
    
    public Object get(String key) {
        return properties.get(key);
    }
    
    public IResource[] getAllSelectedResources() {
        IResource[] resources = (IResource[])get(SELECTED_RESOURCES);
        if (resources == null) {
            resources = getResources(selection);
            put(SELECTED_RESOURCES, resources);
        }
        return resources;
    }

    /*
     * This method gets the resources from the given objects.
     * It does so in a manner that is consistent with how the workbench does it.
     * Tha is, it first uses IContributionResourceAdapter, then IResource,
     * then ResourceMapping.
     */
    private IResource[] getResources(Object[] objects) {
        return Utils.getContributedResources(objects);
    }
    
   public IResource[] getNonoverlappingSelectedResources() {
        IResource[] resources = (IResource[])get(NONOVERLAPPING_SELECTED_RESOURCES);
        if (resources == null) {
            resources = getNonOverlapping(getAllSelectedResources());
            put (NONOVERLAPPING_SELECTED_RESOURCES, resources);
        }
        return resources;
    }
        
    public ICVSResource getCVSResourceFor(IResource resource) {
        Map map = (Map)get(CVS_RESOURCE_MAP);
        if (map == null) {
            map = new HashMap();
            put(CVS_RESOURCE_MAP, map);
        }
        ICVSResource cvsResource = (ICVSResource)map.get(resource);
        if (cvsResource == null) {
            cvsResource = CVSWorkspaceRoot.getCVSResourceFor(resource);
            map.put(resource, cvsResource);
        }
        return cvsResource;
    }
    
    /**
     * Method getNonOverlapping ensures that a resource is not covered more than once.
     * @param resources
     * @return IResource[]
     */
    public static IResource[] getNonOverlapping(IResource[] resources) {
        if (resources == null || resources.length == 0 || resources.length == 1) {
            return resources;
        }
        // Sort the resources so the shortest paths are first
        List sorted = new ArrayList();
        sorted.addAll(Arrays.asList(resources));
        Collections.sort(sorted, new Comparator() {
            public int compare(Object arg0, Object arg1) {
                IResource resource0 = (IResource) arg0;
                IResource resource1 = (IResource) arg1;
                return resource0.getFullPath().segmentCount() - resource1.getFullPath().segmentCount();
            }
            public boolean equals(Object arg0) {
                return false;
            }
        });
        // Collect all non-overlapping resources
        List coveredPaths = new ArrayList();
        for (Iterator iter = sorted.iterator(); iter.hasNext();) {
            IResource resource = (IResource) iter.next();
            IPath resourceFullPath = resource.getFullPath();
            boolean covered = false;
            for (Iterator it = coveredPaths.iterator(); it.hasNext();) {
                IPath path = (IPath) it.next();
                if(path.isPrefixOf(resourceFullPath)) {
                    covered = true;
                }
            }
            if (covered) {
                // if the resource is covered by a parent, remove it
                iter.remove();
            } else {
                // if the resource is a non-covered folder, add it to the covered paths
                if (resource.getType() == IResource.FOLDER) {
                    coveredPaths.add(resource.getFullPath());
                }
            }
        }
        return (IResource[]) sorted.toArray(new IResource[sorted.size()]);
    }
    
}
