package SW9.presentations;

import SW9.utility.helpers.DragHelper;
import SW9.utility.helpers.MouseTrackable;
import SW9.utility.mouse.MouseTracker;
import javafx.beans.binding.DoubleBinding;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.fxml.FXMLLoader;
import javafx.fxml.JavaFXBuilderFactory;
import javafx.scene.Parent;
import javafx.scene.input.MouseButton;
import javafx.scene.layout.Pane;
import javafx.scene.shape.Line;

import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;

public class CanvasPresentation extends Pane implements MouseTrackable {

    public static final int GRID_SIZE = 25;
    
    private final DoubleProperty x = new SimpleDoubleProperty();
    private final DoubleProperty y = new SimpleDoubleProperty(0);
    private final MouseTracker mouseTracker = new MouseTracker(this);

    public CanvasPresentation() {
        final URL location = this.getClass().getResource("CanvasPresentation.fxml");

        final FXMLLoader fxmlLoader = new FXMLLoader();
        fxmlLoader.setLocation(location);
        fxmlLoader.setBuilderFactory(new JavaFXBuilderFactory());

        try {
            fxmlLoader.setRoot(this);
            fxmlLoader.load(location.openStream());

            final Grid grid = new Grid(GRID_SIZE);
            getChildren().add(grid);

            DragHelper.makeDraggable(this, mouseEvent -> mouseEvent.getButton().equals(MouseButton.SECONDARY));

        } catch (final IOException ioe) {
            throw new IllegalStateException(ioe);
        }
    }

    @Override
    public DoubleProperty xProperty() {
        return x;
    }

    @Override
    public DoubleProperty yProperty() {
        return y;
    }

    @Override
    public MouseTracker getMouseTracker() {
        return mouseTracker;
    }

    public static class Grid extends Parent {

        private final ArrayList<Line> horizontalLines = new ArrayList<>();
        private final ArrayList<Line> verticalLines = new ArrayList<>();

        public Grid(final int gridSize) {

            // When the scene changes (goes from null to something)
            sceneProperty().addListener((observable, oldScene, newScene) -> {
                // When the width of this scene is being updated
                newScene.widthProperty().addListener((observable1, oldWidth, newWidth) -> {
                    // Remove old lines
                    while (!verticalLines.isEmpty()) {
                        final Line removeLine = verticalLines.get(0);
                        getChildren().remove(removeLine);
                        verticalLines.remove(removeLine);
                    }

                    // Add new lines (to cover the screen, with 1 line in margin in both ends)
                    int i = -1;
                    while (i * gridSize - gridSize < newWidth.doubleValue()) {
                        final Line line = new Line(i * gridSize, 200, i * gridSize, 300);
                        line.getStyleClass().add("grid-line");

                        final DoubleBinding parentXBinding = new DoubleBinding() {
                            {
                                super.bind(getParent().translateXProperty());
                            }

                            @Override
                            protected double computeValue() {
                                final int moveFactor = (int) (getParent().getTranslateX() / gridSize);
                                return -1 * moveFactor * gridSize + gridSize / 2;
                            }
                        };

                        line.layoutXProperty().bind(parentXBinding);
                        line.startYProperty().bind(getParent().layoutYProperty().subtract(getParent().translateYProperty()).subtract(50)); // the 50 is a fix
                        line.endYProperty().bind(getParent().layoutYProperty().subtract(getParent().translateYProperty()).add(getScene().heightProperty()));

                        verticalLines.add(line);
                        i++;
                    }
                    verticalLines.forEach(line -> getChildren().add(line));
                });

                // When the height of this scene is being updated
                newScene.heightProperty().addListener((observable1, oldHeight, newHeight) -> {
                    // Remove old lines
                    while (!horizontalLines.isEmpty()) {
                        final Line removeLine = horizontalLines.get(0);
                        getChildren().remove(removeLine);
                        horizontalLines.remove(removeLine);
                    }

                    // Add new lines (to cover the screen, with 1 line in margin in both ends)
                    int i = -1;
                    while (i * gridSize - gridSize < newHeight.doubleValue()) {
                        final Line line = new Line(200, i * gridSize, 300, i * gridSize);
                        line.getStyleClass().add("grid-line");

                        final DoubleBinding parentYBinding = new DoubleBinding() {
                            {
                                super.bind(getParent().translateYProperty());
                            }

                            @Override
                            protected double computeValue() {
                                final int moveFactor = (int) (getParent().getTranslateY() / gridSize);
                                return -1 * moveFactor * gridSize + gridSize / 2;
                            }
                        };

                        line.layoutYProperty().bind(parentYBinding);
                        line.startXProperty().bind(getParent().layoutXProperty().subtract(getParent().translateXProperty()));
                        line.endXProperty().bind(getParent().layoutXProperty().subtract(getParent().translateXProperty()).add(getScene().widthProperty()));

                        horizontalLines.add(line);
                        i++;
                    }
                    horizontalLines.forEach(line -> getChildren().add(line));
                });
            });

        }

    }
}

@@ -682,20 +682,16 @@
 E = 25;%0A
-    
 %0A    pri
@@ -1291,81 +1291,23 @@
     
-f
 in
-al Grid grid = new Grid(GRID_SIZE);%0A            getChildren().add(grid
+itializeGrid(
 );%0A%0A
@@ -1520,32 +1520,178 @@
        %7D%0A    %7D%0A%0A
+    private void initializeGrid() %7B%0A        final Grid grid = new Grid(GRID_SIZE);%0A        getChildren().add(grid);%0A        grid.toBack();%0A    %7D%0A%0A
     @Override%0A  

Fix a bug where the grid would be the front most object