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

import be.iminds.ilabt.jfed.experiment.Experiment;
import be.iminds.ilabt.jfed.experiment.util.ExperimentUtils;
import be.iminds.ilabt.jfed.experimenter_gui.timeline.controller.TimelineController;
import be.iminds.ilabt.jfed.experimenter_gui.timeline.model.TimelineModel;
import be.iminds.ilabt.jfed.experimenter_gui.timeline.model.TimelineModelFactory;
import be.iminds.ilabt.jfed.experimenter_gui.timeline.view.output.ControllerOutput;
import be.iminds.ilabt.jfed.experimenter_gui.slice.DetailsTabHost;
import be.iminds.ilabt.jfed.experimenter_gui.slice.NodePropertiesDialogFactory;
import be.iminds.ilabt.jfed.experimenter_gui.util.ImageUtil;
import be.iminds.ilabt.jfed.preferences.JFedGuiPreferences;
import be.iminds.ilabt.jfed.rspec.model.ModelRspecType;
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.javafx_impl.FXModelRspec;
import be.iminds.ilabt.jfed.rspec.model.javafx_impl.FXRspecNode;
import javafx.beans.value.ObservableBooleanValue;
import javafx.geometry.Orientation;
import javafx.scene.control.SplitPane;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Objects;

/**
 * @author Cedric
 */
public class TimelineView extends SplitPane implements TimelineComponent {
    private static final Logger LOG = LoggerFactory.getLogger(TimelineView.class);


    private final FXModelRspec modelRspec;
    private final TimelineModel model;
    private final TimelineController controller;
    private final TimelineTimeline experimentController;
    private final TimelineTreeView experimentList;
    private ControllerOutput experimentOutput;

    /**
     * Constructor which initiates ("offline") editor-view of timeline.
     *
     * @param modelRspec Model that can be used as source for editing
     */
    public TimelineView(FXModelRspec modelRspec, NodePropertiesDialogFactory nodePropertiesDialog,
                        ImageUtil imageUtil, TimelineModelFactory timelineModelFactory, JFedGuiPreferences jFedPreferences) {
        this.modelRspec = modelRspec;
        controller = new TimelineController(jFedPreferences);
        model = timelineModelFactory.createControllerModel(modelRspec);
        controller.setModel(model);
        controller.setView(this);

        experimentController = new TimelineTimeline(model, controller);
        experimentList = new TimelineTreeView(model, controller, nodePropertiesDialog, imageUtil);


        this.setDividerPosition(0, 0.2);
        this.setOrientation(Orientation.HORIZONTAL);
        this.getItems().addAll(experimentList, experimentController);

        SplitPane.setResizableWithParent(experimentList, false);

        initializeTimelineGui();

    }

    /**
     * Constructor for use as an "online" experiment recorder
     */
    public TimelineView(Experiment experiment, DetailsTabHost detailsTabHost,
                        NodePropertiesDialogFactory nodePropertiesDialog, ImageUtil imageUtil,
                        TimelineModelFactory timelineModelFactory, JFedGuiPreferences jFedPreferences) {

        this.modelRspec = ExperimentUtils.getMergedRspec(experiment).getModelRspec(ModelRspecType.FX);
        controller = new TimelineController(jFedPreferences);
        model = timelineModelFactory.createControllerModel(experiment);
        controller.setModel(model);
        controller.setView(this);

        experimentController = new TimelineTimeline(model, controller);
        experimentList = new TimelineTreeView(model, controller, nodePropertiesDialog, imageUtil);

        experimentOutput = new ControllerOutput(detailsTabHost, controller, imageUtil);


        getItems().addAll(experimentList, experimentController);
        setOrientation(Orientation.HORIZONTAL);
        setDividerPosition(0, 0.2);


        SplitPane.setResizableWithParent(experimentList, false);

        initializeTimelineGui();
    }

    private void initializeTimelineGui() {
        if (modelRspec.getBarrierSegments().isEmpty()) {
            controller.addBarrierSegment();
        } else {
            for (ExperimentBarrierSegment b : modelRspec.getBarrierSegments()) {
                update(UpdateEvent.ADD_BARRIER, b);
            }
        }

        for (FXRspecNode n : model.getNodes()) {
            update(UpdateEvent.ADD_NODE, n);
            for (ExperimentCommand c : n.getCommands()) {
                LOG.debug("initializeTimelineGui() for command for node with id " + c.getNodeUniqueID() + " on node with id " + n.getUniqueId());
                assert Objects.equals(n.getUniqueId(), c.getNodeUniqueID()) :
                        "node id\"" + n.getUniqueId() + "\" does not equal command node id \"" + c.getNodeUniqueID() + "\"";
                update(UpdateEvent.ADD_COMMAND, c);
            }
        }
    }

    @Override
    public void update(UpdateEvent event, Object o) {
        experimentList.update(event, o);
        experimentController.update(event, o);

        //experimentOutput is not used in the editor mode
        if (!model.isOfflineEditor()) {
            experimentOutput.update(event, o);
        }
    }

    public FXModelRspec getModelRspec() {
        return modelRspec;
    }

    public TimelineController getController() {
        return controller;
    }

    public TimelineModel getModel() {
        return model;
    }

    public ObservableBooleanValue onlineProperty() {
        return model.allConnectableProperty();
    }
}
