package ctbrec.ui.settings;

import java.io.IOException;
import java.util.HashSet;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import ctbrec.recorder.postprocessing.PostProcessor;
import ctbrec.ui.controls.DirectorySelectionBox;
import ctbrec.ui.controls.ProgramSelectionBox;
import ctbrec.ui.settings.api.ExclusiveSelectionProperty;
import ctbrec.ui.settings.api.Preferences;
import ctbrec.ui.settings.api.PreferencesStorage;
import ctbrec.ui.settings.api.Setting;
import ctbrec.ui.settings.api.SimpleDirectoryProperty;
import ctbrec.ui.settings.api.SimpleFileProperty;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.ListProperty;
import javafx.beans.property.LongProperty;
import javafx.beans.property.Property;
import javafx.beans.property.StringProperty;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.control.RadioButton;
import javafx.scene.control.TextField;
import javafx.scene.control.ToggleGroup;
import javafx.scene.layout.HBox;
import javafx.util.converter.NumberStringConverter;

public abstract class AbstractPostProcessingPaneFactory {

    private static final Logger LOG = LoggerFactory.getLogger(AbstractPostProcessingPaneFactory.class);
    private PostProcessor pp;
    Set<Property<?>> properties = new HashSet<>();

    public abstract Preferences doCreatePostProcessorPane(PostProcessor pp);

    public Preferences createPostProcessorPane(PostProcessor pp) {
        this.pp = pp;
        return doCreatePostProcessorPane(pp);
    }

    class MapPreferencesStorage implements PreferencesStorage {

        @Override
        public void save(Preferences preferences) throws IOException {
            for (Property<?> property : properties) {
                String key = property.getName();
                Object value = preferences.getSetting(key).get().getProperty().getValue();
                LOG.debug("{}={}", key, value.toString());
                pp.getConfig().put(key, value.toString());
            }
        }

        @Override
        public void load(Preferences preferences) {
            // no op
        }

        @Override
        public Node createGui(Setting setting) throws Exception {
            Property<?> prop = setting.getProperty();
            if (prop instanceof ExclusiveSelectionProperty) {
                return createRadioGroup(setting);
            } else if (prop instanceof SimpleDirectoryProperty) {
                return createDirectorySelector(setting);
            } else if (prop instanceof SimpleFileProperty) {
                return createFileSelector(setting);
            } else if (prop instanceof IntegerProperty) {
                return createIntegerProperty(setting);
            } else if (prop instanceof LongProperty) {
                return createLongProperty(setting);
            } else if (prop instanceof BooleanProperty) {
                return createBooleanProperty(setting);
            } else if (prop instanceof ListProperty) {
                return createComboBox(setting);
            } else if (prop instanceof StringProperty) {
                return createStringProperty(setting);
            } else {
                return new Label("Unsupported Type for key " + setting.getKey() + ": " + setting.getProperty());
            }
        }
    }

    private Node createRadioGroup(Setting setting) {
        ExclusiveSelectionProperty prop = (ExclusiveSelectionProperty) setting.getProperty();
        ToggleGroup toggleGroup = new ToggleGroup();
        RadioButton optionA = new RadioButton(prop.getOptionA());
        optionA.setSelected(prop.getValue());
        optionA.setToggleGroup(toggleGroup);
        RadioButton optionB = new RadioButton(prop.getOptionB());
        optionB.setSelected(!optionA.isSelected());
        optionB.setToggleGroup(toggleGroup);
        optionA.selectedProperty().bindBidirectional(prop);
        HBox row = new HBox();
        row.getChildren().addAll(optionA, optionB);
        HBox.setMargin(optionA, new Insets(5));
        HBox.setMargin(optionB, new Insets(5));
        return row;
    }

    private Node createFileSelector(Setting setting) {
        ProgramSelectionBox programSelector = new ProgramSelectionBox("");
        //        programSelector.fileProperty().addListener((obs, o, n) -> saveValue(() -> {
        //            String path = n;
        //            Field field = Settings.class.getField(setting.getKey());
        //            String oldValue = (String) field.get(settings);
        //            if (!Objects.equals(path, oldValue)) {
        //                field.set(settings, path);
        //                config.save();
        //            }
        //        }));
        StringProperty property = (StringProperty) setting.getProperty();
        programSelector.fileProperty().bindBidirectional(property);
        return programSelector;
    }

    private Node createDirectorySelector(Setting setting) {
        DirectorySelectionBox directorySelector = new DirectorySelectionBox("");
        directorySelector.prefWidth(400);
        //        directorySelector.fileProperty().addListener((obs, o, n) -> saveValue(() -> {
        //            String path = n;
        //            Field field = Settings.class.getField(setting.getKey());
        //            String oldValue = (String) field.get(settings);
        //            if (!Objects.equals(path, oldValue)) {
        //                field.set(settings, path);
        //                config.save();
        //            }
        //        }));
        StringProperty property = (StringProperty) setting.getProperty();
        directorySelector.fileProperty().bindBidirectional(property);
        return directorySelector;
    }

    @SuppressWarnings("unchecked")
    private Node createStringProperty(Setting setting) {
        TextField ctrl = new TextField();
        ctrl.textProperty().bindBidirectional(setting.getProperty());
        return ctrl;
    }

    @SuppressWarnings("unchecked")
    private Node createIntegerProperty(Setting setting) {
        TextField ctrl = new TextField();
        Property<Number> prop = setting.getProperty();
        ctrl.textProperty().bindBidirectional(prop, new NumberStringConverter());
        return ctrl;
    }

    @SuppressWarnings("unchecked")
    private Node createLongProperty(Setting setting) {
        TextField ctrl = new TextField();
        Property<Number> prop = setting.getProperty();
        ctrl.textProperty().bindBidirectional(prop, new NumberStringConverter());
        return ctrl;
    }

    private Node createBooleanProperty(Setting setting) {
        CheckBox ctrl = new CheckBox();
        BooleanProperty prop = (BooleanProperty) setting.getProperty();
        ctrl.selectedProperty().bindBidirectional(prop);
        return ctrl;
    }

    @SuppressWarnings({ "rawtypes", "unchecked" })
    private Node createComboBox(Setting setting) throws NoSuchFieldException, IllegalAccessException {
        ListProperty<?> listProp = (ListProperty<?>) setting.getProperty();
        ComboBox<Object> comboBox = new ComboBox(listProp);
        //        Field field = Settings.class.getField(setting.getKey());
        //        Object value = field.get(Config.getInstance().getSettings());
        //        if (StringUtil.isNotBlank(value.toString())) {
        //            if (setting.getConverter() != null) {
        //                comboBox.getSelectionModel().select(setting.getConverter().convertTo(value));
        //            } else {
        //                comboBox.getSelectionModel().select(value);
        //            }
        //        }
        //        comboBox.valueProperty().addListener((obs, oldV, newV) -> saveValue(() -> {
        //            if (setting.getConverter() != null) {
        //                field.set(settings, setting.getConverter().convertFrom(newV));
        //            } else {
        //                field.set(settings, newV);
        //            }
        //            config.save();
        //        }));
        return comboBox;
    }
}
