package be.iminds.ilabt.jfed.experimenter_gui.slice.actions;

import be.iminds.ilabt.jfed.experimenter_gui.slice.ExperimentViewController;
import be.iminds.ilabt.jfed.experimenter_gui.ui.progressbar.TaskService;
import be.iminds.ilabt.jfed.experimenter_gui.util.FileUtils;
import be.iminds.ilabt.jfed.preferences.GuiPreferenceKey;
import be.iminds.ilabt.jfed.preferences.JFedPreferences;
import be.iminds.ilabt.jfed.rspec.model.LoginService;
import be.iminds.ilabt.jfed.rspec.model.ModelRspecType;
import be.iminds.ilabt.jfed.rspec.model.javafx_impl.FXModelRspec;
import be.iminds.ilabt.jfed.rspec.model.javafx_impl.FXRspecInterface;
import be.iminds.ilabt.jfed.rspec.model.javafx_impl.FXRspecNode;
import be.iminds.ilabt.jfed.ui.javafx.dialogs.JFDialogs;
import javafx.application.Platform;
import javafx.concurrent.Task;
import javafx.stage.FileChooser;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.List;

/**
 * User: twalcari
 * Date: 2/2/2015
 * Time: 16:32
 */
public class SaveManifestAction extends ExperimentViewAction {

    private final boolean saveToCsv;

    private final TaskService taskService;
    private final JFedPreferences jFedPreferences;

    SaveManifestAction(ExperimentViewController experimentViewController, boolean saveToCsv, TaskService taskService, JFedPreferences jFedPreferences) {
        super(experimentViewController);
        this.saveToCsv = saveToCsv;
        this.taskService = taskService;
        this.jFedPreferences = jFedPreferences;
    }


    @Override
    public void run() {
        assert (Platform.isFxApplicationThread());

        FileChooser fc = new FileChooser();
        fc.setTitle("Save Manifest RSpec");
        fc.getExtensionFilters().addAll(FileUtils.MANIFEST_RSPEC_EXTENSION_FILTER, FileUtils.CSV_EXTENSION_FILTER);
        fc.setSelectedExtensionFilter(
                saveToCsv ? FileUtils.CSV_EXTENSION_FILTER : FileUtils.MANIFEST_RSPEC_EXTENSION_FILTER);


        fc.setInitialFileName(experiment.getSlice().getName() + FileUtils.MANIFEST_RSPEC_EXTENSION);


        //set the default save dir
        if (System.getProperty("user.home") != null)
            fc.setInitialDirectory(new File(System.getProperty("user.home")));

        if (jFedPreferences.containsPreference(GuiPreferenceKey.PREF_RSPECFOLDER_REQUEST)) {
            File dir = new File(jFedPreferences.getString(GuiPreferenceKey.PREF_RSPECFOLDER_REQUEST));
            if (dir.exists()) {
                fc.setInitialDirectory(dir);
            }
        }

        File file = fc.showSaveDialog(parentWindow);

        if (file == null)
            return;

        //save folder in which the file was saved for future file selector dialogs
        jFedPreferences.setString(GuiPreferenceKey.PREF_RSPECFOLDER_REQUEST,
                file.getParentFile().getAbsolutePath());

        Task<Void> saveTask;

        if (fc.getSelectedExtensionFilter() == FileUtils.CSV_EXTENSION_FILTER) {
            //first make sure that the file-extension is present
            if (!file.getAbsolutePath().endsWith(FileUtils.CSV_EXTENSION))
                file = new File(file.getAbsolutePath() + FileUtils.CSV_EXTENSION);

            saveTask = new SaveCSVTask(file);
        } else {
            //first make sure that the file-extension is present
            if (!file.getAbsolutePath().endsWith(FileUtils.MANIFEST_RSPEC_EXTENSION))
                file = new File(file.getAbsolutePath() + FileUtils.MANIFEST_RSPEC_EXTENSION);

            saveTask = new SaveManifestTask(file);
        }

        taskService.submitTask(saveTask);
    }


    public class SaveCSVTask extends Task<Void> {
        private final File file;

        public SaveCSVTask(File file) {
            this.file = file;
        }

        @Override
        protected Void call() throws Exception {
            updateMessage("Saving experiment CSV in \"" + file.getAbsolutePath() + "\"");

            try (FileWriter writer = new FileWriter(file)) {

                //find the maximum numbers of interfaces on one node
                FXModelRspec modelRspec =
                        experiment.getSlice().getManifestRspec().getModelRspec(ModelRspecType.FX);

                int maxIfacesCount =
                        modelRspec.getNodes().stream()
                                .map(FXRspecNode::getInterfaces)
                                .mapToInt(List::size).max().orElse(0);

                //find max number of ip-addresses per interface
                int[] ipAddressesCount = new int[maxIfacesCount];
                for (FXRspecNode node : modelRspec.getNodes()) {
                    for (int i = 0; i < node.getInterfaces().size(); i++) {
                        ipAddressesCount[i] =
                                Math.max(node.getInterfaces().get(i).getIpAddresses().size(), ipAddressesCount[i]);
                    }
                }

                writer.write("node_id, authentication_type, hostname, port, username");

                for (int i = 0; i < maxIfacesCount; i++) {
                    writer.write(", iface_id#, mac#".replace("#", Integer.toString(i + 1)));

                    for (int j = 0; j < ipAddressesCount[i]; j++) {
                        writer.write(
                                ", ip#_$, netmask#_$"
                                        .replace("#", Integer.toString(i + 1))
                                        .replace("$", Integer.toString(j + 1)));
                    }
                }
                writer.write(String.format("%n"));
                writer.flush();

                for (FXRspecNode node : modelRspec.getNodes()) {

                    //create trailing ifaces part

                    StringBuilder ifacesStringBuilder = new StringBuilder();

                    for (int i = 0; i < maxIfacesCount; i++) {
                        if (i < node.getInterfaces().size()) {
                            FXRspecInterface iface = node.getInterfaces().get(i);

                            ifacesStringBuilder.append(iface.getClientId()).append(",").append(iface.getMacAddress()).append(",");

                            for (int j = 0; j < ipAddressesCount[i]; j++) {
                                if (j < iface.getIpAddresses().size()) {
                                    FXRspecInterface.IpAddress address = iface.getIpAddresses().get(j);
                                    ifacesStringBuilder.append(address.getAddress()).append(",").append(address.getNetmask()).append(",");
                                } else {
                                    ifacesStringBuilder.append(",,");
                                }
                            }
                        } else {
                            ifacesStringBuilder.append(",,");
                            for (int j = 0; j < ipAddressesCount[i]; j++) {
                                ifacesStringBuilder.append(",,");
                            }
                        }
                    }


                    for (LoginService loginService : node.getLoginServices()) {
                        writer.write(String.format("%s, %s, %s, %s, %s,",
                                node.getClientId(),
                                loginService.getAuthentication(),
                                loginService.getHostname(),
                                loginService.getPort(),
                                loginService.getUsername()
                        ));
                        writer.write(ifacesStringBuilder.toString());
                        writer.write(String.format("%n"));

                        writer.flush();
                    }
                }

                writer.close();
            } catch (final IOException ex) {
                Platform.runLater(() -> JFDialogs.create()
                        .owner(experimentViewController)
                        .message("An error occurred while saving the experiment CSV.")
                        .masthead("Error")
                        .title("Error")
                        .showException(ex));
            }
            updateMessage("Successfully saved experiment CSV in \"" + file.getAbsolutePath() + "\"");

            return null;
        }
    }


    public class SaveManifestTask extends Task<Void> {

        private final File file;

        public SaveManifestTask(File file) {
            this.file = file;
        }

        @Override
        protected Void call() throws Exception {
            updateMessage("Saving experiment manifest in \"" + file.getAbsolutePath() + "\"");

            try (FileWriter writer = new FileWriter(file)) {
                writer.write(experiment.getSlice().getManifestRspec().getRspecXmlString());
                writer.flush();
                writer.close();
            } catch (final IOException ex) {
                Platform.runLater(() -> JFDialogs.create()
                        .owner(experimentViewController)
                        .message("An error occurred while saving the manifest rspec.")
                        .masthead("Error")
                        .title("Error")
                        .showException(ex));
            }
            updateMessage("Successfully saved manifest experiment in \"" + file.getAbsolutePath() + "\"");

            return null;
        }
    }

}
