package org.javafxdata.control.cell;

import java.util.Map;

import javafx.beans.property.ReadOnlyBooleanWrapper;
import javafx.beans.property.ReadOnlyDoubleWrapper;
import javafx.beans.property.ReadOnlyFloatWrapper;
import javafx.beans.property.ReadOnlyIntegerWrapper;
import javafx.beans.property.ReadOnlyLongWrapper;
import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.beans.property.ReadOnlyStringWrapper;
import javafx.beans.value.ObservableValue;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableColumn.CellDataFeatures;
import javafx.util.Callback;

/**
 * A value factory implementation for use with the TableColumn 
 * {@link TableColumn#cellValueFactoryProperty() cell value factory} concept.
 * 
 * @author Jonathan Giles
 * @param <T> The type of the ObservableValue that should be returned.
 */
public class MapValueFactory<T> implements Callback<CellDataFeatures<Map,T>, ObservableValue<T>> {

    private final Object key;
    
    public MapValueFactory(final Object key) {
        this.key = key;
    }
    
    @Override public ObservableValue<T> call(CellDataFeatures<Map, T> cdf) {
        Object value = cdf.getValue().get(key);
        
        // ideally the map will contain observable values directly, and in which
        // case we can just return this observable value.
        if (value instanceof ObservableValue) {
            return (ObservableValue)value;
        }
        
        // Often time there is special case code to deal with specific observable
        // value types, so we try to wrap in the most specific type.
        if (value instanceof Boolean) {
            return (ObservableValue<T>) new ReadOnlyBooleanWrapper((Boolean)value);
        } else if (value instanceof Integer) {
            return (ObservableValue<T>) new ReadOnlyIntegerWrapper((Integer)value);
        } else if (value instanceof Float) {
            return (ObservableValue<T>) new ReadOnlyFloatWrapper((Float)value);
        } else if (value instanceof Long) {
            return (ObservableValue<T>) new ReadOnlyLongWrapper((Long)value);
        } else if (value instanceof Double) {
            return (ObservableValue<T>) new ReadOnlyDoubleWrapper((Double)value);
        } else if (value instanceof String) {
            return (ObservableValue<T>) new ReadOnlyStringWrapper((String)value);
        }
        
        // fall back to an object wrapper
        return new ReadOnlyObjectWrapper<T>((T)value);
    }
}
