package com.valkryst.Schillsaver.mvc.controller;

import com.valkryst.Schillsaver.SceneManager;
import com.valkryst.Schillsaver.job.Job;
import com.valkryst.Schillsaver.job.JobBuilder;
import com.valkryst.Schillsaver.log.LogLevel;
import com.valkryst.Schillsaver.mvc.model.JobModel;
import com.valkryst.Schillsaver.mvc.view.JobView;
import com.valkryst.Schillsaver.setting.Settings;
import javafx.collections.FXCollections;
import javafx.event.Event;
import javafx.event.EventHandler;
import javafx.scene.control.Alert;
import javafx.scene.control.ButtonType;
import javafx.scene.control.ListView;
import javafx.scene.input.Dragboard;
import javafx.scene.input.TransferMode;
import javafx.stage.FileChooser;
import lombok.NonNull;

import javax.swing.*;
import java.awt.*;
import java.io.File;
import java.util.List;

public class JobController extends Controller implements EventHandler {
    /**
     * Constructs a new JobController.
     *
     * @param model
     *          The model.
     *
     * @throws NullPointerException
     *          If the sceneManager or settings is null.
     */
    public JobController(final @NonNull JobModel model) {
        super(model, new JobView(model));
        addEventHandlers();

        final JobView view = (JobView) super.getView();
        for (final File file : model.getFiles()) {
            view.getFileList().getItems().add(file.getName());
        }
    }

    /** Sets all of the view's controls to use this class as their event handler. */
    private void addEventHandlers() {
        final JobModel model = (JobModel) super.getModel();
        final JobView view = (JobView) super.getView();

        view.getButton_addFiles().setOnAction(this);
        view.getButton_removeSelectedFiles().setOnAction(this);
        view.getButton_selectOutputFolder().setOnAction(this);
        view.getButton_accept().setOnAction(this);
        view.getButton_cancel().setOnAction(this);

        // Allow the user to drag and drop files onto the view in order to
        // add those files to the Job's file list.
        view.getPane().setOnDragOver(event -> {
            if (event.getDragboard().hasFiles()) {
                event.acceptTransferModes(TransferMode.COPY);
            } else {
                event.consume();
            }
        });

        view.getPane().setOnDragDropped(event -> {
            final Dragboard db = event.getDragboard();

            if (db.hasFiles()) {
                for (final File file : db.getFiles()) {
                    view.getFileList().getItems().add(file.getName());
                    model.addFile(file);
                }

                event.setDropCompleted(true);
            } else {
                event.setDropCompleted(false);
            }

            event.consume();
        });
    }

    @Override
    public void handle(final Event event) {
        final JobModel model = (JobModel) super.getModel();
        final JobView view = (JobView) super.getView();

        final Object source = event.getSource();

        if (source.equals(view.getButton_addFiles())) {
            addFiles();
            return;
        }

        if (source.equals(view.getButton_removeSelectedFiles())) {
            removeSelectedFiles();
            return;
        }

        if (source.equals(view.getButton_selectOutputFolder())) {
            selectOutputFolder();
            return;
        }

        if (source.equals(view.getButton_accept())) {
            final Controller previousController = SceneManager.getInstance().getPreviousController();
            final MainController controller = (MainController) previousController;

            if (model.getFiles().size() == 0) {
                SceneManager.getInstance().swapToPreviousScene();
                return;
            }

            final Job job = createJob();

            if (job != null) {
                controller.addJob(job);
                SceneManager.getInstance().swapToPreviousScene();
            }

            return;
        }

        if (source.equals(view.getButton_cancel())) {
            SceneManager.getInstance().swapToPreviousScene();
        }
    }

    /**
     * Creates a new job using the data entered in the view.
     *
     * @return
     *         The job.
     */
    private Job createJob() {
        final JobModel model = (JobModel) super.getModel();
        final JobView view = (JobView) super.getView();

        final JobBuilder builder = new JobBuilder();
        builder.setName(view.getTextField_jobName().getText());
        builder.setOutputDirectory(view.getTextField_outputFolder().getText());
        builder.setFiles(model.getFiles());
        builder.setEncodeJob(view.getComboBox_jobType().getSelectionModel().isSelected(0));

        Job job = null;

        try {
            job = builder.build();
        } catch (final NullPointerException e) {
            final Alert alert = new Alert(Alert.AlertType.ERROR, "Unable to build job. Have you selected an output directory and/or any files?", ButtonType.OK);
            alert.showAndWait();

            Settings.getInstance().getLogger().log(e, LogLevel.ERROR);
        } catch (final IllegalArgumentException e) {
            final Alert alert = new Alert(Alert.AlertType.ERROR, "The output directory does not exist and\ncould not be created.", ButtonType.OK);
            alert.showAndWait();

            Settings.getInstance().getLogger().log(e, LogLevel.ERROR);
        }

        return job;
    }

    /** Opens a file chooser for the user to add files to the job. */
    private void addFiles() {
        final var fileChooser = new FileChooser();
        fileChooser.setTitle("Job File Selection");
        fileChooser.setInitialDirectory(new File(System.getProperty("user.home")));

        final var selectedFiles = fileChooser.showOpenMultipleDialog(SceneManager.getInstance().getStage());

        if (selectedFiles != null) {
            final JobModel model = (JobModel) super.getModel();
            final JobView view = (JobView) super.getView();

            selectedFiles.forEach(model::addFile);

            // Add all of the files to the list:
            for (final File f : selectedFiles) {
                view.getFileList().getItems().add(f.getName());
            }
        }
    }

    /** Removes all files selected within the view's file list. */
    private void removeSelectedFiles() {
        final JobModel model = (JobModel) super.getModel();
        final JobView view = (JobView) super.getView();

        final ListView<String> fileList = view.getFileList();
        final List<String> selectedFiles = FXCollections.observableArrayList(fileList.getSelectionModel().getSelectedItems());

        for (final String fileName : selectedFiles) {
            view.getFileList().getItems().remove(fileName);
            model.removeFilesWithFilename(fileName);
        }

        fileList.getSelectionModel().clearSelection();
    }

    /**
     * Open a file chooser for the user to select an output directory for
     * the job.
     */
    private void selectOutputFolder() {
        final JFileChooser fileChooser = new JFileChooser();
        fileChooser.setDragEnabled(false);
        fileChooser.setMultiSelectionEnabled(false);
        fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);

        fileChooser.setDialogTitle("Directory Selection");
        fileChooser.setCurrentDirectory(new File(System.getProperty("user.home")));

        fileChooser.setApproveButtonText("Accept");

        try {
            int returnVal = fileChooser.showOpenDialog(null);

            if (returnVal == JFileChooser.APPROVE_OPTION) {
                final JobView view = (JobView) super.getView();
                view.getTextField_outputFolder().setText(fileChooser.getSelectedFile().getPath() + "/");
            }
        } catch(final HeadlessException e) {
            final Alert alert = new Alert(Alert.AlertType.ERROR, "There was an issue selecting an output folder.\nSee the log file for more information.", ButtonType.OK);
            alert.showAndWait();

            Settings.getInstance().getLogger().log(e, LogLevel.ERROR);
        }
    }
}