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.experimenter_gui.timeline.util.TimeCoordinateTranslator;
import be.iminds.ilabt.jfed.rspec.model.controller.ExperimentBarrierSegment;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.scene.Cursor;
import javafx.scene.layout.Pane;
import javafx.scene.shape.Rectangle;

/**
 * @author Cedric
 */
public class TimelineElapsedTimeIndicator extends Pane implements TimelineComponent {
    private final Rectangle indicator;
    private Rectangle indicatorEdge;

    private final TimelineModel model;
    private final TimelineController controller;
    private final ExperimentBarrierSegment barrierSegment;
    private final TimelineBarrierSegment parent;
    private final TimeCoordinateTranslator translator = TimeCoordinateTranslator.getInstance();
    private final double EDGE_WIDTH = 4;

    private ChangeListener<Boolean> modelOnlineListener, barrierCompletedListener;

    private boolean dragging = false;

    public TimelineElapsedTimeIndicator(final TimelineModel model, TimelineController controller, ExperimentBarrierSegment b, TimelineBarrierSegment parent) {
        this.model = model;
        this.controller = controller;
        this.barrierSegment = b;
        this.parent = parent;

        setPickOnBounds(false);

        indicator = new Rectangle();
        indicator.getStyleClass().add("indicator");
        indicator.heightProperty().bind(heightProperty());
        indicator.setWidth(0);
        indicator.setOpacity(0.5);
        indicator.setMouseTransparent(true);

        this.getChildren().add(indicator);

        if (!model.isOfflineEditor()) {
            indicatorEdge = new Rectangle();
            indicatorEdge.setX(0 - EDGE_WIDTH / 2);
            indicatorEdge.setY(0);
            indicatorEdge.heightProperty().bind(heightProperty());
            indicatorEdge.setWidth(EDGE_WIDTH);
            indicatorEdge.getStyleClass().add("indicatorEdge");
            indicatorEdge.setCursor(Cursor.H_RESIZE);


            initMouseListeners();
            this.getChildren().add(indicatorEdge);

            modelOnlineListener = new ChangeListener<Boolean>() {

                @Override
                public void changed(ObservableValue<? extends Boolean> ov, Boolean t, Boolean t1) {
                    if (!t1) {
                        getChildren().remove(indicatorEdge);
                        model.allConnectableProperty().removeListener(this);
                    }
                }
            };
            model.allConnectableProperty().addListener(modelOnlineListener);

            barrierCompletedListener = (ov, t, t1) -> {
                if (t1) {
                    indicatorEdge.setX(TimelineElapsedTimeIndicator.this.parent.getWidth() - EDGE_WIDTH / 2);
                    indicator.setWidth(TimelineElapsedTimeIndicator.this.parent.getWidth());
                }
            };

            barrierSegment.completedProperty().addListener(barrierCompletedListener);
        }


    }

    private void initMouseListeners() {
        indicatorEdge.setOnMousePressed(t -> {
            indicatorEdge.getStyleClass().add("indicatorEdgeDragged");
            dragging = true;
        });

        indicatorEdge.setOnMouseDragged(t -> {
            double x;
            if (t.getX() < 0) {
                x = 0;
            } else if (t.getX() > parent.getWidth()) {
                x = parent.getWidth();
            } else {
                x = t.getX();
            }
            indicatorEdge.setX(x - EDGE_WIDTH / 2);
        });

        indicatorEdge.setOnMouseReleased(t -> {
            dragging = false;
            indicatorEdge.getStyleClass().removeAll("indicatorEdgeDragged");

            controller.setElapsedTime(translator.translateWidthToTime(indicatorEdge.getX() + EDGE_WIDTH / 2), barrierSegment);
        });
    }

    @Override
    public void update(UpdateEvent event, Object o) {
        if (event == UpdateEvent.UPDATE_TIME || event == UpdateEvent.UPDATE_ZOOM || event == UpdateEvent.REWIND_TIME) {
            double newWidth;
            if (barrierSegment.isCompleted()) {
                newWidth = getWidth();
            }
            else if (barrierSegment.getStarted().get()) {
                newWidth = translator.translateTimeToWidth(model.getElapsedTime());
                newWidth = (newWidth > getWidth() ? getWidth() : newWidth);
            } else
                newWidth = 0;

            if (!dragging && model.allConnectableProperty().get())
                indicatorEdge.setX(newWidth - EDGE_WIDTH / 2);
            indicator.setWidth(newWidth);
        } else if (event == UpdateEvent.ADD_COMMAND) {
            if (barrierSegment.isCompleted()) {
                double newWidth = getWidth();
                if (!dragging && model.allConnectableProperty().get())
                    indicatorEdge.setX(newWidth - EDGE_WIDTH / 2);
                indicator.setWidth(newWidth);
            }
        } else if (event == UpdateEvent.CLEAR) {
            indicator.setWidth(0);
            if (model.allConnectableProperty().get())
                indicatorEdge.setX(0 - EDGE_WIDTH / 2);
        } else if (event == UpdateEvent.UNREGISTER) {
            if (!model.isOfflineEditor()) {
                model.allConnectableProperty().removeListener(modelOnlineListener);
                barrierSegment.completedProperty().removeListener(barrierCompletedListener);
            }
        }
    }

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

        try {
            if (barrierSegment.isCompleted()) {
                if (!model.isOfflineEditor()) {
                    indicatorEdge.setX(parent.getWidth() - EDGE_WIDTH / 2);
                }
                indicator.setWidth(parent.getWidth());
            }
        } catch (NullPointerException ex) {
            //sometimes this gives a random nullpointer for no apparent reason
        }

    }


}
