package nl.tudelft.dnainator.ui.views;

import java.io.File;
import java.io.IOException;

import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.geometry.Bounds;
import javafx.geometry.Point2D;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.transform.Affine;
import javafx.scene.transform.NonInvertibleTransformException;
import javafx.scene.transform.Scale;
import javafx.scene.transform.Transform;
import javafx.scene.transform.Translate;
import nl.tudelft.dnainator.graph.impl.Neo4jSingleton;
import nl.tudelft.dnainator.ui.models.GraphItem;
import nl.tudelft.dnainator.ui.models.ModelItem;
import nl.tudelft.dnainator.ui.widgets.ExceptionDialog;
import nl.tudelft.dnainator.ui.widgets.ViewContext;

import org.neo4j.io.fs.FileUtils;

/**
 * This class is the View part of the MVC pattern.
 */
public class View extends Pane {
	private static final double SCALE = .1;

	@FXML
	private BorderPane root;

	private Affine scale;
	private Translate toCenter;
	private Translate translate;
	private Transform worldToCamera;

	private ModelItem mi;

	/**
	 * Creates a new view instance.
	 */
	public View() {
		try {
			FileUtils.deleteRecursively(new File(Neo4jSingleton.DB_PATH));
		} catch (IOException e) {
			e.printStackTrace();
		}

		loadFXML();
		getStyleClass().add("view");
		setOnContextMenuRequested(e -> {
			ViewContext.getInstance().show(View.this, e.getScreenX(), e.getScreenY());
			e.consume();
		});

		toCenter = new Translate();
		widthProperty().addListener((o, v1, v2) -> toCenter.setX(v2.intValue() / 2));
		heightProperty().addListener((o, v1, v2) -> toCenter.setY(v2.intValue() / 2));

		translate = new Translate();
		translate.setOnTransformChanged(e -> worldToCamera = worldToCamera());

		scale = new Affine(new Scale(SCALE, SCALE));
		scale.setOnTransformChanged(e -> worldToCamera = worldToCamera());

		mi = new GraphItem();
		mi.getTransforms().add(toCenter);
		mi.getTransforms().add(translate);
		mi.getTransforms().add(scale);
		getChildren().add(mi);
	}

	private void loadFXML() {
		FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/ui/fxml/view.fxml"));
		fxmlLoader.setRoot(this);

		try {
			fxmlLoader.load();
		} catch (IOException e) {
			new ExceptionDialog(root, e, "Can not load the View!");
		}
	}

	/**
	 * Returns the concatenated transform from world coordinates to camera coordinates.
	 * @return	the concatenated transform
	 */
	private Transform worldToCamera() {
		return toCenter.createConcatenation(scale).createConcatenation(translate);
	}

	/**
	 * Transforms a given bounding box from camera coordinates to world coordinates.
	 * @param b	the given bounding box
	 * @return	the transformed bounding box
	 */
	public Bounds cameraToWorld(Bounds b) {
		Bounds world = null;
		try {
			world = worldToCamera().inverseTransform(b);
		} catch (NonInvertibleTransformException e) {
			e.printStackTrace();
		}
		return world;
	}

	/**
	 * Pan the camera by the amount given by the delta vector.
	 * @param delta	the delta vector
	 */
	public void pan(Point2D delta) {
		translate.setX(translate.getX() + delta.getX());
		translate.setY(translate.getY() + delta.getY());
		mi.update(cameraToWorld(getLayoutBounds()));
	}

	/**
	 * Zoom the camera by the amount given by zoom.
	 * @param zoom		the amount to zoom in
	 * @param center	the center of the zoom, in camera space
	 */
	public void zoom(Double zoom, Point2D center) {
		try {
			center = new Point2D(center.getX() - toCenter.getX(),
						center.getY() - toCenter.getY());
			center = new Point2D(center.getX() - translate.getX(),
						center.getY() - translate.getTy());
			center = scale.inverseTransform(center);
			scale.append(new Scale(1 + zoom, 1 + zoom, center.getX(), center.getY()));
		} catch (NonInvertibleTransformException e) {
			e.printStackTrace();
		}
		mi.update(cameraToWorld(getLayoutBounds()));
	}
}

@@ -1044,42 +1044,8 @@
 ate;
-%0A%09private Transform worldToCamera;
 %0A%0A%09p
@@ -1667,192 +1667,49 @@
 ;%0A%09%09
-translate.setOnTransformChanged(e -%3E worldToCamera = worldToCamera());%0A%0A%09%09scale = new Affine(new Scale(SCALE, SCALE));%0A%09%09scale.setOnTransformChanged(e -%3E worldToCamera = worldToCamera(
+scale = new Affine(new Scale(SCALE, SCALE
 ));%0A
@@ -2346,20 +2346,24 @@
 enation(
-scal
+translat
 e).creat
@@ -2377,24 +2377,20 @@
 enation(
-translat
+scal
 e);%0A%09%7D%0A%0A
@@ -3294,59 +3294,31 @@
 r = 
-new Point2D(center.getX() - toCenter.getX(),%0A%09%09%09%09%09%09
+scale.inverseTransform(
 cent
@@ -3319,25 +3319,25 @@
 m(center.get
-Y
+X
 () - toCente
@@ -3342,48 +3342,8 @@
 ter.
-getY());%0A%09%09%09center = new Point2D(center.
 getX
@@ -3365,16 +3365,17 @@
 getX(),%0A
+%09
 %09%09%09%09%09%09ce
@@ -3393,68 +3393,41 @@
  - t
-ransla
+oCen
 te
+r
 .get
-Ty());%0A%09%09%09center = scale.inverseTransform(center
+Y() - translate.getY()
 );%0A%09

Fix viewport transform method.