/* SelectionModel.java
 * =========================================================================
 * This file is part of the GrInvIn project - http://www.grinvin.org
 * 
 * Copyright (C) 2005-2008 Universiteit Gent
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or (at
 * your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 * 
 * A copy of the GNU General Public License can be found in the file
 * LICENSE.txt provided with the source distribution of this program (see
 * the META-INF directory in the source jar). This license can also be
 * found on the GNU website at http://www.gnu.org/licenses/gpl.html.
 * 
 * If you did not receive a copy of the GNU General Public License along
 * with this program, contact the lead developer, or write to the Free
 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301, USA.
 */

package org.grinvin.gui;

import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Observable;

/**
 * Keeps track of a selection of items of similar type. This model allows
 * multiple selections (but no interval selections).<p>
 */
public class SelectionModel<T> extends Observable implements Iterable<T> {
    
    /**
     * Tell all observers that the selection has changed.
     */
    protected void notifySelectionChanged () {
        setChanged();
        notifyObservers();
    }

    // Keeps track of the current selection
    protected Collection<T> selection ;
    
    /**
     * Clear the selection.
     */
    public void clearSelection() {
        if (! selection.isEmpty()) {
            selection.clear ();
            notifySelectionChanged ();
        }
    }

    /**
     * Check whether the current selection is empty.
     * @return {@code true} if and only if the selection is empty.
     */
    public boolean isSelectionEmpty() {
        return selection.isEmpty();
    }

    /**
     * Check whether the given element is selected.
     * @return whether the given item is currently selected.
     */
    public boolean isSelected (T item) {
        return selection.contains(item);
    }
 
    /**
     * Toggles the selection. 
     * Removes the given item from the selection if it already
     * belongs to the selection. Otherwise adds it to the selection.
     */
    public void toggleSelection (T item) {
        if (!selection.remove(item)) {
            selection.add(item);
        }
        notifySelectionChanged ();
    }

    /**
     * Set the selection to be (only) the given object.
     */
    public void setSelection (T v) {
        if (selection.size() !=1 || !selection.contains(v)) {
            selection.clear ();
            selection.add (v);
            notifySelectionChanged ();
        }
    }
    
    /**
     * Set the selection to be the given set of objects.
     */
    public void setSelection (Collection<T> set) {
        selection.clear ();
        selection.addAll (set);
        notifySelectionChanged ();
    }
    
    /**
     * Toggle the selection for the given set of objects.
     * The resulting selection is the symmetric difference of the current
     * selection and the given set of elements.
     */
    public void toggleSelection (Collection<T> c) {
        if (c.isEmpty())
            return ;
        for (T el: c)
            if (!selection.remove (el))
                selection.add (el);
        notifySelectionChanged ();
    }
    
    
    /**
     * Add the given object to the selection. Does nothing when the object was
     * already selected.
     */
    public void addSelection (T c) {
        if (selection.add(c)) 
            notifySelectionChanged ();
    }

    /**
     * Add the objects in the given set to the selection.
     */
    public void addSelection (Collection<T> c) {
        if (selection.addAll(c)) 
            notifySelectionChanged ();
    }
    
    /**
     * Return the number of items that is currently selected.
     */
    public int selectionCount () {
        return selection.size ();
    }
    
    /**
     * Return an iterator that iterates over all selected elements.
     * Clients should not use the {@code remove}-method of this iterator
     * as removals will not be signaled to the observers of this model.
     */
    public Iterator<T> iterator () {
        return selection.iterator();
    }
    
    /** Creates a new selection model with empty selection. Uses a
     * {@link HashSet} to keep track of the selected objects. */
    public SelectionModel() {
        this.selection = new HashSet <T>();
    }
    
    /** Creates a new SelectionModel which uses the given
     * collection to keep track of the selected objects.
     */
    public SelectionModel (Collection<T> c) {
        this.selection = c;
    }
    
    /**
     * Add the current selection to the given collection.
     * @param c Collection to which all currently selected elements
     * are to be added.
     * @return A reference to argument c
     */
    public Collection<T> getSelection (Collection<T> c) {
        c.addAll (selection);
        return c;
    }
}
    
