package org.nwapw.abacus.fx;

import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.scene.control.*;
import javafx.scene.text.Text;
import javafx.util.Callback;
import org.nwapw.abacus.Abacus;
import org.nwapw.abacus.number.NumberInterface;
import org.nwapw.abacus.tree.TreeNode;


/**
 * The controller for the abacus FX UI, responsible
 * for all the user interaction.
 */
public class AbacusController {

    /**
     * Constant string that is displayed if the text could not be lexed or parsed.
     */
    private static final String ERR_SYNTAX = "Syntax Error";
    /**
     * Constant string that is displayed if the tree could not be reduced.
     */
    private static final String ERR_EVAL = "Evaluation Error";

    @FXML
    private TableView<HistoryModel> historyTable;
    @FXML
    private TableColumn<HistoryModel, String> inputColumn;
    @FXML
    private TableColumn<HistoryModel, String> parsedColumn;
    @FXML
    private TableColumn<HistoryModel, String> outputColumn;
    @FXML
    private Text outputText;
    @FXML
    private TextField inputField;
    @FXML
    private Button inputButton;
    @FXML
    private ComboBox<String> numberImplementationBox;
    @FXML
    private ListView enabledPluginView;
    @FXML
    private Button reloadButton;

    /**
     * The list of history entries, created by the users.
     */
    private ObservableList<HistoryModel> historyData;

    /**
     * The abacus instance used for calculations and all
     * other main processing code.
     */
    private ObservableList<String> numberImplementationOptions;

    private Abacus abacus;

    @FXML
    public void initialize(){
        Callback<TableColumn<HistoryModel, String>, TableCell<HistoryModel, String>> cellFactory =
                param -> new CopyableCell<>();

        historyData = FXCollections.observableArrayList();
        historyTable.setItems(historyData);
        numberImplementationOptions = FXCollections.observableArrayList();
        numberImplementationBox.setItems(numberImplementationOptions);
        numberImplementationBox.valueProperty().addListener((observable, oldValue, newValue)
                -> {
            abacus.getConfiguration().setNumberImplementation(newValue);
            abacus.getConfiguration().saveTo(Abacus.CONFIG_FILE);
        });
        historyTable.getSelectionModel().setCellSelectionEnabled(true);
        inputColumn.setCellFactory(cellFactory);
        inputColumn.setCellValueFactory(cell -> cell.getValue().inputProperty());
        parsedColumn.setCellFactory(cellFactory);
        parsedColumn.setCellValueFactory(cell -> cell.getValue().parsedProperty());
        outputColumn.setCellFactory(cellFactory);
        outputColumn.setCellValueFactory(cell -> cell.getValue().outputProperty());

        abacus = new Abacus();
        numberImplementationOptions.addAll(abacus.getPluginManager().getAllNumbers());
        String actualImplementation = abacus.getConfiguration().getNumberImplementation();
        String toSelect = (numberImplementationOptions.contains(actualImplementation)) ? actualImplementation : "naive";
        numberImplementationBox.getSelectionModel().select(toSelect);
    }

    @FXML
    private void performCalculation(){
        inputButton.setDisable(true);
        TreeNode constructedTree = abacus.parseString(inputField.getText());
        if(constructedTree == null){
            outputText.setText(ERR_SYNTAX);
            inputButton.setDisable(false);
            return;
        }
        NumberInterface evaluatedNumber = abacus.evaluateTree(constructedTree);
        if(evaluatedNumber == null){
            outputText.setText(ERR_EVAL);
            inputButton.setDisable(false);
            return;
        }
        outputText.setText(evaluatedNumber.toString());
        historyData.add(new HistoryModel(inputField.getText(), constructedTree.toString(), evaluatedNumber.toString()));

        inputButton.setDisable(false);
        inputField.setText("");
    }

}

@@ -162,16 +162,67 @@
 trol.*;%0A
+import javafx.scene.control.cell.CheckBoxListCell;%0A
 import j
@@ -273,16 +273,52 @@
 llback;%0A
+import javafx.util.StringConverter;%0A
 import o
@@ -1359,16 +1359,34 @@
 ListView
+%3CToggleablePlugin%3E
  enabled
@@ -1751,215 +1751,1092 @@
     
-private Abacus abacus;%0A%0A    @FXML%0A    public void initialize()%7B%0A        Callback%3CTableColumn%3CHistoryModel, String%3E, TableCell%3CHistoryModel, String%3E%3E cellFactory =%0A                param -%3E new CopyableCell%3C%3E(
+/**%0A     * The list of plugin objects that can be toggled on and off,%0A     * and, when reloaded, get added to the plugin manager's black list.%0A     */%0A    private ObservableList%3CToggleablePlugin%3E enabledPlugins;%0A%0A    private Abacus abacus;%0A%0A    @FXML%0A    public void initialize()%7B%0A        Callback%3CTableColumn%3CHistoryModel, String%3E, TableCell%3CHistoryModel, String%3E%3E cellFactory =%0A                param -%3E new CopyableCell%3C%3E();%0A        Callback%3CListView%3CToggleablePlugin%3E, ListCell%3CToggleablePlugin%3E%3E pluginCellFactory =%0A                param -%3E new CheckBoxListCell%3C%3E(ToggleablePlugin::enabledProperty, new StringConverter%3CToggleablePlugin%3E() %7B%0A                    @Override%0A                    public String toString(ToggleablePlugin object) %7B%0A                        return object.getClassName().substring(object.getClassName().lastIndexOf('.') + 1);%0A                    %7D%0A%0A                    @Override%0A                    public ToggleablePlugin fromString(String string) %7B%0A                        return new ToggleablePlugin(true, string);%0A                    %7D%0A                %7D
 );%0A%0A
@@ -3413,32 +3413,207 @@
 nEnabled(true);%0A
+        enabledPlugins = FXCollections.observableArrayList();%0A        enabledPluginView.setItems(enabledPlugins);%0A        enabledPluginView.setCellFactory(pluginCellFactory);%0A
         inputCol

Add a check box list cell generator.