package ctbrec.ui.settings;

import ctbrec.Config;
import ctbrec.recorder.postprocessing.*;
import ctbrec.ui.controls.Dialogs;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.scene.control.Button;
import javafx.scene.control.ChoiceDialog;
import javafx.scene.control.ListView;
import javafx.scene.control.Tooltip;
import javafx.scene.image.Image;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.util.Optional;

public class PostProcessingStepPanel extends GridPane {

    private final Config config;

    private static final Class<?>[] POST_PROCESSOR_CLASSES = new Class<?>[]{ // @formatter: off
            Copy.class,
            Rename.class,
            Move.class,
            Remux.class,
            Script.class,
            DeleteOriginal.class,
            DeleteTooShort.class,
            RemoveKeepFile.class,
            CreateContactSheet.class
    }; // @formatter: on

    ListView<PostProcessor> stepListView;
    ObservableList<PostProcessor> stepList;

    Button up;
    Button down;

    Button add;
    Button remove;
    Button edit;

    public PostProcessingStepPanel(Config config) {
        this.config = config;
        initGui();
    }

    private void initGui() {
        setHgap(5);
        vgapProperty().bind(hgapProperty());

        up = createUpButton();
        down = createDownButton();
        add = createAddButton();
        remove = createRemoveButton();
        edit = createEditButton();
        VBox buttons = new VBox(5, add, edit, up, down, remove);

        stepList = FXCollections.observableList(config.getSettings().postProcessors);
        stepList.addListener((ListChangeListener<PostProcessor>) change -> {
            try {
                config.save();
            } catch (IOException e) {
                Dialogs.showError(getScene(), "Couldn't save configuration", "An error occurred while saving the configuration", e);
            }
        });
        stepListView = new ListView<>(stepList);
        GridPane.setHgrow(stepListView, Priority.ALWAYS);

        add(stepListView, 0, 0);
        add(buttons, 1, 0);

        stepListView.getSelectionModel().selectedIndexProperty().addListener((obs, oldV, newV) -> {
            int idx = newV.intValue();
            boolean noSelection = idx == -1;
            up.setDisable(noSelection || idx == 0);
            down.setDisable(noSelection || idx == stepList.size() - 1);
            edit.setDisable(noSelection);
            remove.setDisable(noSelection);
        });
    }

    private Button createUpButton() {
        Button button = createButton("\u25B4", "Move step up");
        button.setOnAction(evt -> {
            int idx = stepListView.getSelectionModel().getSelectedIndex();
            PostProcessor selectedItem = stepListView.getSelectionModel().getSelectedItem();
            stepList.remove(idx);
            stepList.add(idx - 1, selectedItem);
            stepListView.getSelectionModel().select(idx - 1);
        });
        return button;
    }

    private Button createDownButton() {
        Button button = createButton("\u25BE", "Move step down");
        button.setOnAction(evt -> {
            int idx = stepListView.getSelectionModel().getSelectedIndex();
            PostProcessor selectedItem = stepListView.getSelectionModel().getSelectedItem();
            stepList.remove(idx);
            stepList.add(idx + 1, selectedItem);
            stepListView.getSelectionModel().select(idx + 1);
        });
        return button;
    }

    private Button createAddButton() {
        Button button = createButton("+", "Add a new step");
        button.setDisable(false);
        button.setOnAction(evt -> {
            PostProcessor[] options = createOptions();
            ChoiceDialog<PostProcessor> choice = new ChoiceDialog<>(options[0], options);
            choice.setTitle("New Post-Processing Step");
            choice.setHeaderText("Select the new step type");
            choice.setResizable(true);
            choice.setWidth(600);
            choice.getDialogPane().setMinWidth(400);
            Stage stage = (Stage) choice.getDialogPane().getScene().getWindow();
            stage.getScene().getStylesheets().addAll(getScene().getStylesheets());
            InputStream icon = Dialogs.class.getResourceAsStream("/icon.png");
            stage.getIcons().add(new Image(icon));

            Optional<PostProcessor> result = choice.showAndWait();
            result.ifPresent(pp -> PostProcessingDialogFactory.openNewDialog(pp, getScene(), stepList));
            saveConfig();
        });
        return button;
    }

    private void saveConfig() {
        try {
            config.save();
        } catch (IOException e) {
            Dialogs.showError(getScene(), "Post-Processing", "Couldn't save post-processing step", e);
        }
    }

    private PostProcessor[] createOptions() {
        try {
            PostProcessor[] options = new PostProcessor[POST_PROCESSOR_CLASSES.length];
            for (int i = 0; i < POST_PROCESSOR_CLASSES.length; i++) {
                Class<?> cls = POST_PROCESSOR_CLASSES[i];
                PostProcessor pp;
                pp = (PostProcessor) cls.getDeclaredConstructor().newInstance();
                options[i] = pp;
            }
            return options;
        } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException
                | SecurityException e) {
            Dialogs.showError(getScene(), "Create post-processor selection", "Error while reaing in post-processing options", e);
            return new PostProcessor[0];
        }
    }

    private Button createRemoveButton() {
        Button button = createButton("-", "Remove selected step");
        button.setOnAction(evt -> {
            PostProcessor selectedItem = stepListView.getSelectionModel().getSelectedItem();
            if (selectedItem != null) {
                stepList.remove(selectedItem);
            }
        });
        return button;
    }

    private Button createEditButton() {
        Button button = createButton("\u270E", "Edit selected step");
        button.setOnAction(evt -> {
            PostProcessor selectedItem = stepListView.getSelectionModel().getSelectedItem();
            PostProcessingDialogFactory.openEditDialog(selectedItem, getScene(), stepList);
            stepListView.refresh();
            saveConfig();
        });
        return button;
    }

    private Button createButton(String text, String tooltip) {
        Button b = new Button(text);
        b.setTooltip(new Tooltip(tooltip));
        b.setDisable(true);
        b.setPrefSize(32, 32);
        return b;
    }
}
