package org.javafxdata.control.cell;

import static org.javafxdata.control.cell.ComboBoxCell.createComboBox;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.control.TableCell;
import javafx.util.Callback;

/**
 * <p>A class containing a {@link TableCell} implementation that draws a {@link ComboBox}
 * node inside the cell.
 * 
 * <img src="http://www.javafxdata.org/screenshots/ComboBoxCell-TableView.PNG" 
 * style="float: right; padding: 0px 0px 0px 3px;"/>
 * 
 * <p>By default, the ComboBoxTableCell is rendered as a {@link Label} when not being edited, and as a ComboBox 
 * when in editing mode. The ComboBox will, by default, stretch to fill the entire table cell.
 * 
 * <p>To create a ComboBoxTableCell, it is necessary to provide zero or more items that will be shown to the user when the
 * {@link ComboBox} menu is showing. These items must be of the same type as the TableColumn.
 * 
 * @param <T> The type of the elements contained within the TableColumn.
 * @author <a href="http://www.jonathangiles.net">Jonathan Giles</a>
 */
public class ComboBoxTableCell<S,T> extends TableCell<S,T> {
        
    private final ObservableList<T> items;

    private ComboBox<T> comboBox;

    private final Callback<T, String> toString;
    
    /**
     * Creates a default {@link ComboBoxTableCell} instance with the given items
     * being used to populate the {@link ComboBox} when it is shown.
     * 
     * @param items The items to show in the ComboBox popup menu when selected by the user.
     */
    public ComboBoxTableCell(T... items) {
        this(FXCollections.observableArrayList(items));
    }
    
    /**
     * Creates a {@link ComboBoxTableCell} instance with the given items
     * being used to populate the {@link ComboBox} when it is shown, and the toString
     * {@link Callback} being used to convert the item in to a user-readable form.
     * 
     * @param toString A {@link Callback} that can convert an item of type T into a 
     *      user-readable string so that it may then be shown in the ComboBox popup menu.
     * @param items The items to show in the ComboBox popup menu when selected by the user.
     */
    public ComboBoxTableCell(Callback<T, String> toString, T... items) {
        this(toString, FXCollections.observableArrayList(items));
    }
    
    /**
     * Creates a default {@link ComboBoxTableCell} instance with the given items
     * being used to populate the {@link ComboBox} when it is shown.
     * 
     * @param items The items to show in the ComboBox popup menu when selected by the user.
     */
    public ComboBoxTableCell(ObservableList<T> items) {
        this(null, items);
    }

    /**
     * Creates a {@link ComboBoxTableCell} instance with the given items
     * being used to populate the {@link ComboBox} when it is shown, and the toString
     * {@link Callback} being used to convert the item in to a user-readable form.
     * 
     * @param toString A {@link Callback} that can convert an item of type T into a 
     *      user-readable string so that it may then be shown in the ComboBox popup menu.
     * @param items The items to show in the ComboBox popup menu when selected by the user.
     */
    public ComboBoxTableCell(Callback<T, String> toString, ObservableList<T> items) {
        this.items = items;
        this.toString = toString != null ? toString : ComboBoxCell.<T>defaultToString();
    }
    
    /** {@inheritDoc} */
    @Override public void startEdit() {
        if (! isEditable() || ! getTableView().isEditable() || ! getTableColumn().isEditable()) {
            return;
        }
        
        if (comboBox == null) {
            comboBox = createComboBox(this, items);
        }
        
        comboBox.getSelectionModel().select(getItem());
        
        super.startEdit();
        setText(null);
        setGraphic(comboBox);
    }

    /** {@inheritDoc} */
    @Override public void cancelEdit() {
        super.cancelEdit();
        
        setText(toString.call(getItem()));
        setGraphic(null);
    }
    
    /** {@inheritDoc} */
    @Override public void updateItem(T item, boolean empty) {
        super.updateItem(item, empty);
        ComboBoxCell.updateItem(this, comboBox, toString);
    }
}