/*
 *    GeoTools - OpenSource mapping toolkit
 *    http://geotools.org
 *    (C) 2004-2006, GeoTools Project Managment Committee (PMC)
 *    (C) 2004, 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;
 *    version 2.1 of the License.
 *
 *    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.
 *
 *    This package contains documentation from OpenGIS specifications.
 *    OpenGIS consortium's work is fully acknowledged here.
 */
package org.geotools.referencing;

// J2SE dependencies
import java.util.AbstractMap;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

// OpenGIS dependencies
import org.opengis.referencing.IdentifiedObject;
import org.opengis.referencing.operation.CoordinateOperation;

// Geotools dependencies
import org.geotools.util.MapEntry;
import org.geotools.referencing.operation.AbstractCoordinateOperation;


/**
 * An immutable map fetching all properties from the specified identified object.
 * Calls to {@code get} methods are forwarded to the appropriate
 * {@link IdentifiedObject} method.
 *
 * @since 2.1
 * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/referencing/src/main/java/org/geotools/referencing/Properties.java $
 * @version $Id: Properties.java 28264 2007-12-05 21:53:08Z desruisseaux $
 * @author Martin Desruisseaux
 */
final class Properties extends AbstractMap {
    /**
     * The object where all properties come from.
     */
    private final IdentifiedObject info;

    /**
     * The entries set. Will be constructed only when first needed.
     */
    private transient Set entries;
    
    /**
     * Creates new properties from the specified identified object.
     */
    public Properties(final IdentifiedObject info) {
        this.info = info;
        AbstractIdentifiedObject.ensureNonNull("info", info);
    }

    /**
     * Returns true if this map contains a mapping for the specified key.
     */
    public boolean containsKey(final Object key) {
        return get(key) != null;
    }

    /**
     * Returns the value to which this map maps the specified key.
     * Returns null if the map contains no mapping for this key.
     */
    public Object get(final Object key) {
        if (key instanceof String) {
            final String s = ((String) key).trim();
            for (int i=0; i<KEYS.length; i++) {
                if (KEYS[i].equalsIgnoreCase(s)) {
                    return get(i);
                }
            }
        }
        return null;
    }

    /**
     * Returns the value to which this map maps the specified index.
     * Returns null if the map contains no mapping for this index.
     */
    private Object get(final int key) {
        switch (key) {
            case  0: return info.getName();
            case  1: return info.getIdentifiers().toArray(AbstractIdentifiedObject.EMPTY_IDENTIFIER_ARRAY);
            case  2: return info.getAlias()      .toArray(AbstractIdentifiedObject.EMPTY_ALIAS_ARRAY);
            case  3: return info.getRemarks();
            case  4: return (info instanceof CoordinateOperation) ? ((CoordinateOperation) info).getScope()              : null;
            case  5: return (info instanceof CoordinateOperation) ? ((CoordinateOperation) info).getValidArea()          : null;
            case  6: return (info instanceof CoordinateOperation) ? ((CoordinateOperation) info).getOperationVersion()   : null;
            case  7: return (info instanceof CoordinateOperation) ? ((CoordinateOperation) info).getPositionalAccuracy()
                                .toArray(AbstractCoordinateOperation.EMPTY_ACCURACY_ARRAY): null;
            default: return null;
        }
    }

    /**
     * The keys to search for. <STRONG>The index of each element in this array
     * must matches the index searched by {@link #get(int)}.</STRONG>
     *
     * @todo Add properties for {@link IdentifiedObject} sub-interfaces.
     */
    private static final String[] KEYS = {
        /*[0]*/IdentifiedObject    .NAME_KEY,
        /*[1]*/IdentifiedObject    .IDENTIFIERS_KEY,
        /*[2]*/IdentifiedObject    .ALIAS_KEY,
        /*[3]*/IdentifiedObject    .REMARKS_KEY,
        /*[4]*/CoordinateOperation .SCOPE_KEY,
        /*[5]*/CoordinateOperation .DOMAIN_OF_VALIDITY_KEY,
        /*[6]*/CoordinateOperation .OPERATION_VERSION_KEY,
        /*[7]*/CoordinateOperation .COORDINATE_OPERATION_ACCURACY_KEY
    };

    /**
     * Returns a set view of the mappings contained in this map.
     */
    public Set entrySet() {
        if (entries == null) {
            entries = new HashSet(Math.round(KEYS.length/0.75f)+1, 0.75f);
            for (int i=0; i<KEYS.length; i++) {
                final Object value = get(i);
                if (value != null) {
                    entries.add(new MapEntry(KEYS[i], value));
                }
            }
            entries = Collections.unmodifiableSet(entries);
        }
        return entries;
    }
}
