package be.iminds.ilabt.jfed.experimenter_gui.editor.properties.map;

import be.iminds.ilabt.jfed.experimenter_gui.config.JFedGuiConfig;
import be.iminds.ilabt.jfed.experimenter_gui.util.StringConverters;
import be.iminds.ilabt.jfed.util.GeniUrn;
import javafx.animation.SequentialTransition;
import javafx.animation.StrokeTransition;
import javafx.animation.Transition;
import javafx.beans.InvalidationListener;
import javafx.beans.Observable;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.fxml.FXML;
import javafx.geometry.Rectangle2D;
import javafx.scene.Cursor;
import javafx.scene.control.Button;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyCodeCombination;
import javafx.scene.input.KeyCombination;
import javafx.scene.input.ScrollEvent;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.BorderPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Screen;
import javafx.stage.Stage;
import javafx.util.Duration;

import java.util.*;

/**
 * User: twalcari
 * Date: 1/14/2015
 * Time: 10:55
 */
public class ImageMapDialog {
    private static final double MAX_ZOOM = 1.5d;
    private static final double MIN_ZOOM = .01d;

    @FXML
    private BorderPane root;

    private ImageMap<GeniUrn> imageMap;
    private Collection<JFedGuiConfig.ComponentInfo> availableNodes;

    @FXML
    private AnchorPane anchorPane;

    @FXML
    private ScrollPane imageScrollPane;
    @FXML
    private ImageView imageView;

    @FXML
    private ComboBox<JFedGuiConfig.ComponentInfo> nodesComboBox;
    @FXML
    private Button selectNodeButton;
    @FXML
    private Label noLocationLabel;

    @FXML
    private Rectangle selectedNodeRectangle;
    private Transition selectedNodeStrokeTransition;

    private final DoubleProperty zoom = new SimpleDoubleProperty(1.0);

    private JFedGuiConfig.ComponentInfo result = null;
    private Image image;
    private static final Color TRANSPARENT_GRAY = Color.gray(0.3, 0.5);

    @FXML
    private void initialize() {
        nodesComboBox.setConverter(StringConverters.COMPONENT_INFO_CONVERTER);
        noLocationLabel.managedProperty().bind(noLocationLabel.visibleProperty());

        selectNodeButton.disableProperty().bind(nodesComboBox.valueProperty().isNull());

        imageScrollPane.addEventFilter(ScrollEvent.ANY, scrollEvent -> {
            if (scrollEvent.isControlDown()) {
                double newZoom = zoom.get() + scrollEvent.getDeltaY() / 100;

                if (newZoom >= MIN_ZOOM && newZoom <= MAX_ZOOM) {
                    zoom.set(newZoom);
                }
                scrollEvent.consume();
            }
        });

        zoom.addListener(observable -> {
            imageView.setFitWidth(imageView.getImage().getWidth() * zoom.get());

            rectangles.values().forEach(
                    rect -> {
                        rect.setScaleX(zoom.get());
                        rect.setScaleY(zoom.get());
                    }

            );
        });

        root.sceneProperty().addListener(new InvalidationListener() {
            @Override
            public void invalidated(Observable observable) {
                root.getScene().getAccelerators().put(new KeyCodeCombination(KeyCode.D, KeyCombination.ModifierValue.UP, KeyCombination.ModifierValue.DOWN,
                        KeyCombination.ModifierValue.UP, KeyCombination.ModifierValue.UP, KeyCombination.ModifierValue.UP), ImageMapDialog.this::showDebugInfo);
                observable.removeListener(this);
            }
        });


        selectedNodeStrokeTransition =
                new SequentialTransition(new StrokeTransition(Duration.millis(300), selectedNodeRectangle, Color.RED, Color.ORANGE),
                        new StrokeTransition(Duration.millis(300), selectedNodeRectangle, Color.ORANGE, Color.YELLOW),
                        new StrokeTransition(Duration.millis(300), selectedNodeRectangle, Color.YELLOW, Color.GREEN),
                        new StrokeTransition(Duration.millis(300), selectedNodeRectangle, Color.GREEN, Color.BLUE),
                        new StrokeTransition(Duration.millis(300), selectedNodeRectangle, Color.BLUE, Color.INDIGO),
                        new StrokeTransition(Duration.millis(300), selectedNodeRectangle, Color.INDIGO, Color.VIOLET),
                        new StrokeTransition(Duration.millis(300), selectedNodeRectangle, Color.VIOLET, Color.RED));
        selectedNodeStrokeTransition.setCycleCount(-1);
        //selectedNodeStrokeTransition.setAutoReverse(true);

        nodesComboBox.valueProperty().addListener((observable, oldValue, newValue) -> {
            selectedNodeRectangle.xProperty().unbind();
            selectedNodeRectangle.yProperty().unbind();
            selectedNodeRectangle.widthProperty().unbind();
            selectedNodeRectangle.heightProperty().unbind();


            if (newValue != null) {
                Optional<ImageMap.Bounds> bounds = imageMap.getSelectableRegion(newValue.getUrn());

                if(bounds.isPresent()) {
                    selectedNodeRectangle.xProperty().bind(zoom.multiply(bounds.get().getMinX()));
                    selectedNodeRectangle.yProperty().bind(zoom.multiply(bounds.get().getMinY()));
                    selectedNodeRectangle.widthProperty().bind(zoom.multiply(bounds.get().getMaxX() - bounds.get().getMinX()));
                    selectedNodeRectangle.heightProperty().bind(zoom.multiply(bounds.get().getMaxY() - bounds.get().getMinY()));
                    selectedNodeRectangle.setVisible(true);
                    selectedNodeStrokeTransition.playFromStart();
                }
                noLocationLabel.setVisible(!bounds.isPresent());

            } else {
                selectedNodeRectangle.setVisible(false);
                selectedNodeStrokeTransition.stop();

                noLocationLabel.setVisible(false);
            }
        });

    }

    private void showDebugInfo() {
        rectangles.values().forEach(rect -> {
            //rect.setFill(Color.TRANSPARENT);
            rect.setStrokeWidth(2);
            rect.setStroke(Color.BLUE);
            rect.setMouseTransparent(true);
        });

    }

    private final Map<GeniUrn, Rectangle> rectangles = new HashMap<>();

    public void setNodesMap(ImageMap<GeniUrn> imageMap, Collection<JFedGuiConfig.ComponentInfo> availableNodes) {
        assert (this.imageMap == null);
        this.imageMap = imageMap;
        this.availableNodes = availableNodes;

        nodesComboBox.getItems().addAll(availableNodes);

        image = new Image(imageMap.getImageUrl().toExternalForm());
        this.imageView.setImage(image);

        for (Map.Entry<ImageMap.Bounds, GeniUrn> entry : imageMap.getSelectableRegions().entrySet()) {
            ImageMap.Bounds b = entry.getKey();

            Rectangle rect = new Rectangle();

            rect.xProperty().bind(zoom.multiply(b.getMinX()));
            rect.yProperty().bind(zoom.multiply(b.getMinY()));
            rect.widthProperty().bind(zoom.multiply(b.getMaxX() - b.getMinX()));
            rect.heightProperty().bind(zoom.multiply(b.getMaxY() - b.getMinY()));


            Optional<JFedGuiConfig.ComponentInfo> availableNode =
                    availableNodes.stream().filter(node -> Objects.equals(entry.getValue(), node.getUrn())).findFirst();

            if (availableNode.isPresent()) {
                rect.setFill(Color.TRANSPARENT);

                rect.setCursor(Cursor.HAND);
                rect.setOnMouseClicked(mouseEvent -> nodesComboBox.setValue(availableNode.get()));

            } else {
                rect.setFill(TRANSPARENT_GRAY);
            }


            rectangles.put(entry.getValue(), rect);
            anchorPane.getChildren().add(rect);
        }


    }


    @FXML
    private void selectNode() {
        result = nodesComboBox.getValue();
        ((Stage) selectNodeButton.getScene().getWindow()).close();
    }

    public JFedGuiConfig.ComponentInfo getResult() {
        return result;
    }

    public void setupStageSize(Stage dialogStage) {
        //default minimums
        dialogStage.setMinHeight(480);
        dialogStage.setMinWidth(640);

        Rectangle2D primaryScreenBounds = Screen.getPrimary().getVisualBounds();

        double height =
                root.getHeight() < primaryScreenBounds.getHeight() ? root.getHeight() : primaryScreenBounds.getHeight();
        double width =
                root.getWidth() < primaryScreenBounds.getWidth() ? root.getWidth() : primaryScreenBounds.getWidth();

        dialogStage.setX(primaryScreenBounds.getMinX() + (primaryScreenBounds.getWidth() - width) / 2);
        dialogStage.setY(primaryScreenBounds.getMinY() + (primaryScreenBounds.getHeight() - height) / 2);
        dialogStage.setWidth(width);
        dialogStage.setHeight(height);

        //update image scale
        double widthZoomFactor = imageScrollPane.getViewportBounds().getWidth() / image.getWidth();
        double heightZoomFactor = imageScrollPane.getViewportBounds().getHeight() / image.getHeight();

        System.out.println(widthZoomFactor + ", " + heightZoomFactor);

        this.zoom.set(Math.min(Math.min(widthZoomFactor, heightZoomFactor), this.zoom.get()));


    }

    public void setAvailableNodes(Collection<JFedGuiConfig.ComponentInfo> availableNodes) {
        assert (imageMap != null);

        for (Map.Entry<GeniUrn, Rectangle> entry : rectangles.entrySet()) {


        }
    }
}
