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

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.rspec.model.controller.ExperimentBarrierSegment;
import be.iminds.ilabt.jfed.rspec.model.controller.ExperimentCommand;
import be.iminds.ilabt.jfed.rspec.model.javafx_impl.FXRspecNode;
import javafx.scene.layout.Pane;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author Cedric
 */
public class TimelineNodes extends Pane implements TimelineComponent {
    private final static Logger LOG = LoggerFactory.getLogger(TimelineNodes.class);
    //Mapping nodeID -> nodeline
    private final Map<String, TimelineNode> nodes = new HashMap<>();

    //List of nodes, to make sure that after rearranging the nodes, the order is not changed
    //Getting the nodes from the Map nodes will alter the order
    private final List<TimelineNode> nodesList = new ArrayList<>();

    private final TimelineController controller;
    private final TimelineModel model;
    private final ExperimentBarrierSegment barrierSegment;
    private final TimelineBarrierSegment segmentParent;

    public TimelineNodes(TimelineModel model, TimelineController controller, ExperimentBarrierSegment b, TimelineBarrierSegment segmentParent) {
        this.model = model;
        this.controller = controller;
        this.barrierSegment = b;
        this.segmentParent = segmentParent;

        //setPickOnBounds: only accept mouseEvents if they overlap with non-transparant parts of this pane (e.g. a nodeline)
        setPickOnBounds(false);
    }

    public void addNode(String uniqueNodeId) {
        if (nodes.containsKey(uniqueNodeId)) return;

        TimelineNode n =
                model.getTimelineNodeFactory().createControllerNode(uniqueNodeId, model, controller, barrierSegment, segmentParent);
        nodes.put(uniqueNodeId, n);
        nodesList.add(n);
        this.getChildren().add(n);

        updateNodesPlacement();
    }

    private void updateNodesPlacement() {
        int i = 0;
        for (TimelineNode node : nodesList) {
            node.setStartX(3);
            node.setEndX(getWidth() - 3);
            node.setStartY((getHeight() - TimelineBase.TICKMARK_HEIGHT) / (nodes.size() + 1) * (i + 1) + TimelineBase.TICKMARK_HEIGHT);
            node.setEndY((getHeight() - TimelineBase.TICKMARK_HEIGHT) / (nodes.size() + 1) * (i + 1) + TimelineBase.TICKMARK_HEIGHT);
            i++;
        }
    }

    @Override
    public void resize(double d, double d1) {
        super.resize(d, d1);
        updateNodesPlacement();
    }

    public void addCommandToNode(String nodeID, ExperimentCommand command) {
        nodes.get(nodeID).addCommand(command);
    }

    @Override
    public void update(UpdateEvent event, Object o) {
        if (event == UpdateEvent.UPDATE_TIME || event == UpdateEvent.UPDATE_ZOOM || event == UpdateEvent.REWIND_TIME || event == UpdateEvent.CLEAR) {
            for (TimelineNode node : nodes.values()) {
                node.update(event, o);
            }
        } else if (event == UpdateEvent.ADD_COMMAND || event == UpdateEvent.DELETE_COMMAND) {
            if (((ExperimentCommand) o).getBarrierSegmentOrderNumber() == barrierSegment.getOrderNumber()) {
                String uniqueId = ((ExperimentCommand) o).getNodeUniqueID();
                assert uniqueId != null;
                TimelineNode cnode = nodes.get(uniqueId);
                if (cnode != null)
                    cnode.update(event, o);
                else
                    LOG.error("ControllerNode was not found for uniqueId=\"" + uniqueId + "\""); //previously this caused an NPE
            }
        } else if (event == UpdateEvent.ADD_NODE) {
            addNode(((FXRspecNode) o).getUniqueId());
        }
    }

}
