/*
 *    GeoTools - OpenSource mapping toolkit
 *    http://geotools.org
 *    (C) 2005-2006, Geotools Project Managment Committee (PMC)
 *    (C) 2005, Institut de Recherche pour le Développement
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License as published by the Free Software Foundation; either
 *    version 2.1 of the License, or (at your option) any later version.
 *
 *    This library 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
 *    Lesser General Public License for more details.
 */
package org.geotools.gui.swing.referencing;

// J2SE dependencies
import java.util.List;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import javax.swing.ComboBoxModel;
import javax.swing.AbstractListModel;

// OpenGIS dependencies
import org.opengis.referencing.AuthorityFactory;
import org.opengis.referencing.FactoryException;


/**
 * A list of {@link Code}s. This implementation will try to fetch the codes only when
 * first needed. Keep in mind that the collection provided to the constructor may be
 * database backed (not a usual implementation from {@code java.util} package), so it
 * is worth to do lazy loading here.
 *
 * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/extension/widgets-swing/src/main/java/org/geotools/gui/swing/referencing/CodeList.java $
 * @version $Id: CodeList.java 20883 2006-08-07 13:48:09Z jgarnett $
 * @author Martin Desruisseaux
 */
final class CodeList extends AbstractListModel implements ComboBoxModel {
    /**
     * The authority factory, or {@code null} if disposed.
     */
    private AuthorityFactory factory;

    /**
     * The collection of authority codes, or {@code null} if disposed.
     */
    private Collection/*<String>*/ codes;

    /**
     * An iterator over the codes, or {@code null} if not yet created or disposed.
     */
    private Iterator iterator;

    /**
     * The authority codes as {@link Code} objects.
     */
    private final List/*<Code>*/ extracted = new ArrayList();

    /**
     * The selected item, or {@code null} if none.
     */
    private Code selected;

    /**
     * Creates a list for the given codes.
     */
    public CodeList(final AuthorityFactory factory, final Class type) throws FactoryException {
        this.factory  = factory;
        this.codes    = factory.getAuthorityCodes(type);
        if (!codes.isEmpty()) {
            // Note: testing if (!isEmpty()) is much facter than if (size() > 0)
            selected = (Code) getElementAt(0);
        }
    }

    /**
     * Returns the length of the list.
     */
    public int getSize() {
        return (codes != null) ? codes.size() : extracted.size();
    }

    /**
     * Returns the value at the specified index.
     *
     * @todo Change to return type to {@link Code} when we will be allowed to compile for J2SE 1.5.
     */
    public Object getElementAt(final int index) {
        if (codes != null) {
            if (iterator == null) {
                iterator = codes.iterator();
            }
            int size;
            while (index >= (size=extracted.size())) {
                if (!iterator.hasNext()) {
                    assert extracted.size() == codes.size();
                    iterator = null;
                    codes    = null;
                    factory  = null;
                    break;
                }
                extracted.add(new Code(factory, (String) iterator.next(), size));
            }
        }
        return (index>=0 && index<extracted.size()) ? extracted.get(index) : null;
    }

    /**
     * Returns the selected item.
     *
     * @todo Change to return type to {@link Code} when we will be allowed to compile for J2SE 1.5.
     */
    public Object getSelectedItem() {
        return selected;
    }

    /**
     * Set the selected item.
     */
    public void setSelectedItem(final Object code) {
        selected = (Code) code;
        int index = selected.index;
        fireContentsChanged(this, index, index);
    }

    /**
     * Returns an item that can be used as a prototype in a combo box. This operation is
     * expansive and should be run only once.
     *
     * @see javax.swing.JComboBox#setPrototypeDisplayValue
     */
    public String getPrototypeItem() {
        final int size = getSize();
        String prototype = "";
        for (int i=0; i<size; i++) {
            final String candidate = getElementAt(i).toString();
            if (candidate.length() > prototype.length()) {
                prototype = candidate;
            }
        }
        return prototype;
    }
}
