/*
 * $Id$
 *
 * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle,
 * Santa Clara, California 95054, U.S.A. All rights reserved.
 *
 * 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.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 */
package org.jdesktop.swingx.sort;

import java.util.HashMap;
import java.util.Map;
import java.util.logging.Logger;

import org.jdesktop.swingx.renderer.StringValue;
import org.jdesktop.swingx.renderer.StringValues;

/**
 * A writable implemenation of StringValueProvider. Typically, this is created and
 * maintained by a collection view and then passed over to interested parties. It is
 * modeled/implemented after the default renderer maintenance in a JTable.<p>
 * 
 * PENDING JW: for safety - better not implement but return a provider. We probably don't want 
 * readers to frickle around here?.
 * 
 * @author Jeanette Winzenburg
 */
public final class StringValueRegistry implements StringValueProvider {

    @SuppressWarnings("unused")
    private static final Logger LOG = Logger
            .getLogger(StringValueRegistry.class.getName());
    
    private Map<Class<?>, StringValue> perClass;
    private HashMap<Integer, StringValue> perColumn;
    private HashMap<Integer, Class<?>> classPerColumn;
    
    /**
     * {@inheritDoc} <p>
     */
    @Override
    public StringValue getStringValue(int row, int column) {
        StringValue sv = getPerColumnMap().get(column);
        if (sv == null) {
            sv = getStringValueByClass(getClass(row, column));
        }
        if (sv == null) {
            sv = getStringValueByClass(Object.class);
        }
        return sv != null ? sv : StringValues.TO_STRING;
    }

//-------------------- manage     
    /**
     * Sets a StringValue to use for the given column. If the converter is null,
     * the mapping is removed.
     * 
     * @param sv the StringValue to use for the given column.
     * @param column the column index in model coordinates.
     * 
     */
    public void setStringValue(StringValue sv, int column) {
        // PENDING really remove mapping if sv null
        getPerColumnMap().put(column, sv);
    }

    /**
     * Removes all per-column mappings of StringValues.
     * 
     */
    public void clearColumnStringValues() {
        getPerColumnMap().clear();
    }
    
    /**
     * Sets the StringValue to use for the given class. If the converter is null,
     * the mapping is removed.
     * 
     * @param sv the StringValue to use for the given column.
     * @param clazz the class 
     */
    public void setStringValue(StringValue sv, Class<?> clazz) {
        // PENDING really remove mapping if sv null
        getPerClassMap().put(clazz, sv);
    }
    
    /**
     * Returns the StringValue registered for the given class. <p>
     * 
     * <b>This is temporarily exposed for testing only - do not use, it will
     * be removed very soon!</b>
     * 
     * @param clazz the class to find the registered StringValue for
     * @return the StringValue registered for the class, or null if not directly
     *   registered.
     */
    public StringValue getStringValue(Class<?> clazz) {
        return getPerClassMap().get(clazz);
    }
    /**
     * Sets the column class.
     * 
     * @param clazz 
     * @param column index in model coordinates
     */
    public void setColumnClass(Class<?> clazz, int column) {
        getColumnClassMap().put(column, clazz);
    }
    
    /**
     * @param classPerColumn
     */
    public void setColumnClasses(Map<Integer, Class<?>> classPerColumn) {
        this.classPerColumn = classPerColumn != null ? 
                new HashMap<Integer, Class<?>>(classPerColumn) : null;
    }

    /**
     * 
     * @param clazz
     * @return
     */
    private StringValue getStringValueByClass(Class<?> clazz) {
        if (clazz == null) return null;
        StringValue sv = getPerClassMap().get(clazz);
        if (sv != null) return sv;
        return getStringValueByClass(clazz.getSuperclass());
    }

    /**
     * Returns the Class of the column.
     * 
     * @param row
     * @param column
     * @return
     */
    private Class<?> getClass(int row, int column) {
        Class<?> clazz = getColumnClassMap().get(column);
        return clazz != null ? clazz : Object.class;
    }

    /**
     * Returns the Map which stores the per-column Class, lazily 
     * creates one if null.
     * 
     * @return the per-column storage map of Class
     */
     private Map<Integer, Class<?>> getColumnClassMap() {
         if (classPerColumn == null) {
             classPerColumn = new HashMap<Integer, Class<?>>();
         }
         return classPerColumn;
     }

     /**
     * Returns the Map which stores the per-class StringValues, lazily 
     * creates one if null.
     * 
     * @return the per-class storage map of StringValues
     */
    private Map<Class<?>, StringValue> getPerClassMap() {
        if (perClass == null) {
            perClass = new HashMap<Class<?>, StringValue>();
        }
        return perClass;
    }

    /**
     * Returns the Map which stores the per-column StringValues, lazily 
     * creates one if null.
     * 
     * @return the per-column storage map of StringValues
     */
    private Map<Integer, StringValue> getPerColumnMap() {
        if (perColumn == null) {
            perColumn = new HashMap<Integer, StringValue>();
        }
        return perColumn;
    }
}
