package be.iminds.ilabt.jfed.experimenter_gui.timeline.controller;

import be.iminds.ilabt.jfed.experimenter_gui.timeline.controller.action.*;
import be.iminds.ilabt.jfed.experimenter_gui.timeline.model.TimelineModel;
import be.iminds.ilabt.jfed.experiment.SshConnectionPool;
import be.iminds.ilabt.jfed.experimenter_gui.timeline.view.TimelineView;
import be.iminds.ilabt.jfed.experimenter_gui.timeline.view.UpdateEvent;
import be.iminds.ilabt.jfed.preferences.JFedGuiPreferences;
import be.iminds.ilabt.jfed.rspec.model.controller.ExperimentBarrierSegment;
import be.iminds.ilabt.jfed.rspec.model.controller.ExperimentCommand;
import be.iminds.ilabt.jfed.rspec.model.controller.Timestamp;
import be.iminds.ilabt.jfed.rspec.model.javafx_impl.FXRspecNode;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;

import java.util.ArrayList;
import java.util.List;

/**
 *
 * @author Cedric
 */
public class TimelineController {
    private TimelineView view;
    private TimelineModel model;
    private final JFedGuiPreferences jFedPreferences;

    public TimelineController(JFedGuiPreferences jFedPreferences) {
        this.jFedPreferences = jFedPreferences;
    }

    public void startTime(){
        final SshConnectionPool sshConnectionPool = model.getSshConnectionPool();
        if (!sshConnectionPool.allConnectedProperty().get()) {
            view.update(UpdateEvent.GENERAL_MESSAGE, "Waiting for all SSH connection to be ready before starting...");

            final ChangeListener<Boolean> allReadyListener = new ChangeListener<Boolean>() {
                @Override
                public void changed(ObservableValue<? extends Boolean> observableValue, Boolean oldVal, Boolean newVal) {
                    if (newVal == true) {
                        sshConnectionPool.allConnectedProperty().removeListener(this);
                        view.update(UpdateEvent.GENERAL_MESSAGE, "All SSH connection are ready: Starting.");
                        uncheckedStartTime();
                    }
                }
            };

            sshConnectionPool.allConnectedProperty().addListener(allReadyListener);

            sshConnectionPool.initAllConnection();
            return;
        }

        view.update(UpdateEvent.GENERAL_MESSAGE, "All SSH connection are ready: Starting.");
        uncheckedStartTime();
    }

    private void uncheckedStartTime() {
        if(!model.isStarted()){
            model.startTime();
            view.update(UpdateEvent.START_TIME, null);
        }
    }

    public void stopTime(){
        if(model.isStarted()){
            model.stopTime();
            view.update(UpdateEvent.STOP_TIME, null);
        }
    }

    public void rewindTime(){
        model.rewindTime();
        view.update(UpdateEvent.REWIND_TIME, null);
    }

    public void setElapsedTime(long time, ExperimentBarrierSegment b){
        model.setElapsedTime(time, b);
        view.update(UpdateEvent.UPDATE_TIME, null);
    }

    public void addBarrierSegment(ExperimentBarrierSegment b){
        model.addBarrierSegment(b);
        view.update(UpdateEvent.ADD_BARRIER, b);
    }

    public void addBarrierSegment(){
        ExperimentBarrierSegment b = new ExperimentBarrierSegment();
        model.addBarrierSegment(b);
        view.update(UpdateEvent.ADD_BARRIER, b);
    }

    public void deleteBarrierSegment(ExperimentBarrierSegment b){
        while(!b.getCommands().isEmpty())
            deleteCommand(b.getCommands().get(0));

        model.deleteBarrierSegment(b);
        view.update(UpdateEvent.DELETE_BARRIER, b);
    }

    public void addCommand(String commandTag, String nodeUniqueID, int barrierSegmentID, Timestamp time, Timestamp duration, String commandText){
        ExperimentCommand comm = new ExperimentCommand(commandTag, nodeUniqueID,
                barrierSegmentID, commandText, time, duration);
        comm.setSshConnectionProvider(model.getSshConnectionPool());
        addCommand(comm);
    }

    public void addCommand(ExperimentCommand c){
        AddCommandAction a = new AddCommandAction(c);
        executeAction(a);
    }

    public void deleteCommand(ExperimentCommand comm){
        DeleteCommandAction a = new DeleteCommandAction(comm);
        executeAction(a);
    }

    /**
     *
     * @param o1 old
     * @param o2 new
     */
    public void updateCommand(ExperimentCommand o1, ExperimentCommand o2){
        UpdateCommandAction a = new UpdateCommandAction(o1, o2);
        executeAction(a);
    }

    public TimelineView getView() {
        return view;
    }

    public void setView(TimelineView view) {
        this.view = view;
    }

    public TimelineModel getModel() {
        return model;
    }

    public void setModel(TimelineModel model) {
        this.model = model;
    }

    public void zoomIn(){
        executeAction(new ZoomInAction());
    }

    public void zoomOut(){
        executeAction(new ZoomOutAction());
    }

    public void resetZoom(){
        executeAction(new ResetZoomAction());
    }

    public void instantCommand(String tag, String command, List<String> uniqueNodeIds) {
        List<ExperimentCommand> commands = new ArrayList<>();
        for(String uniqueNodeId : uniqueNodeIds){
            ExperimentCommand c = new ExperimentCommand(tag, uniqueNodeId,
                    0, command, new Timestamp(model.getElapsedTime()), new Timestamp(0));
            c.setSshConnectionProvider(model.getSshConnectionPool());
            commands.add(c);
        }
        executeAction(new InstantCommandAction(commands));
    }

    public void stopInstantCommand(ExperimentCommand command){
        model.stopInstantCommand(command);
    }

    private void executeAction(IAction a){
        boolean result = a.execute(model, view);
//        if(result){
//            actions.add(a);
//        } else {
//            //TODO
//        }
    }

    public void showCommandOutput(ExperimentCommand recorderCommand) {
        view.update(UpdateEvent.SHOW_COMMAND_OUTPUT, recorderCommand);
    }

    public void showNodeOutput(FXRspecNode rspecNode) {
        view.update(UpdateEvent.SHOW_NODE_OUTPUT, rspecNode);
    }

    public void saveOutput(List<ExperimentCommand> preSelection){
        SaveOutputAction soa = new SaveOutputAction(preSelection, jFedPreferences);
        executeAction(soa);
    }

    public void saveOutput() {
        SaveOutputAction soa = new SaveOutputAction(jFedPreferences);
        executeAction(soa);
    }

    /**
     * This will call the unregister method from the model and invoke 
     * an UpdateEvent.UNREGISTER on the UI. This makes sure no changeListeners
     * remain bound to the model.
     */
    public void unregister(){
        view.update(UpdateEvent.UNREGISTER, null);
        model.unregister();
    }

}
