package nl.tudelft.dnainator.ui.views;

import java.util.ArrayList;
import java.util.List;

import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import nl.tudelft.dnainator.tree.TreeNode;
import nl.tudelft.dnainator.ui.drawables.phylogeny.AbstractNode;
import nl.tudelft.dnainator.ui.drawables.phylogeny.Edge;
import nl.tudelft.dnainator.ui.drawables.phylogeny.InternalNode;
import nl.tudelft.dnainator.ui.drawables.phylogeny.Label;
import nl.tudelft.dnainator.ui.drawables.phylogeny.LeafNode;

/**
 * An implementation of {@link AbstractView} for displaying a phylogenetic tree.
 */
public class PhylogeneticView extends AbstractView {
	private static final double LEAFHEIGHT = 30;
	private static final int MARGIN = 30;
	private static final double LEVELWIDTH = 3 * MARGIN;
	private static final int SQUARE = 8;
	private static final int OFFSET = 8;
	private ObjectProperty<TreeNode> rootProperty = new SimpleObjectProperty<>(null, "root");
	private double currentLeafY;

	/**
	 * Constructs a new {@link PhylogeneticView}.
	 */
	public PhylogeneticView() {
		super();
		rootProperty().addListener((obj, oldV, newV) -> redraw());
	}

	/**
	 * @param root The {@link TreeNode} to set as root.
	 */
	public final void setRoot(TreeNode root) {
		rootProperty.set(root);
	}

	/**
	 * @return The root node, if any.
	 */
	public final TreeNode getRoot() {
		return rootProperty.get();
	}

	/**
	 * @return The root node property.
	 */
	public ObjectProperty<TreeNode> rootProperty() {
		return rootProperty;
	}

	private void redraw() {
		this.currentLeafY = MARGIN;
		getChildren().add(draw(getRoot(), MARGIN, 0));
	}

	private AbstractNode draw(TreeNode node, double x, double y) {
		List<TreeNode> children = node.getChildren();
		if (children.size() == 0) {
			return drawLeaf(node, x);
		}
		return drawInternal(children, x, y);
	}

	private AbstractNode drawLeaf(TreeNode leaf, double x) {
		double y = currentLeafY;

		Label label = new Label(x + OFFSET, y + OFFSET, leaf.getName());
		getChildren().add(label);
		currentLeafY += LEAFHEIGHT;

		return new LeafNode(leaf, label, x, y, SQUARE);
	}

	private AbstractNode drawInternal(List<TreeNode> children, double x, double y) {
		List<AbstractNode> drawnChildren = new ArrayList<>();

		// Draw all children and attach an outgoing edge.
		for (int i = 0; i < children.size(); i++) {
			AbstractNode node = draw(children.get(i), x + LEVELWIDTH, y + (i * LEAFHEIGHT));
			Edge edge = new Edge(node);

			drawnChildren.add(node);
			node.setIncomingEdge(edge);
			getChildren().addAll(edge, node);
		}

		AbstractNode first = drawnChildren.get(0);
		AbstractNode last = drawnChildren.get(drawnChildren.size() - 1);

		// Draw self and attach children's edges to it.
		InternalNode root = new InternalNode(drawnChildren, x, first.getCenterY()
				+ (last.getCenterY() - first.getCenterY()) / 2, SQUARE);
		for (AbstractNode child : drawnChildren) {
			child.getIncomingEdge().bindTo(root);
		}
		return root;
	}

}

@@ -177,24 +177,126 @@
 ctProperty;%0A
+import javafx.scene.Group;%0Aimport javafx.scene.transform.Affine;%0Aimport javafx.scene.transform.Scale;%0A
 import nl.tu
@@ -775,16 +775,53 @@
 tView %7B%0A
+%09private static final int SCALE = 1;%0A
 %09private
@@ -1147,16 +1147,38 @@
 ntLeafY;
+%0A%09private Group group;
 %0A%0A%09/**%0A%09
@@ -1274,63 +1274,227 @@
 ;%0A%09%09
-rootProperty().addListener((obj, oldV, newV) -%3E redraw(
+group = new Group();%0A%09%09setTransforms(group);%0A%09%09getChildren().add(group);%0A%09%09rootProperty().addListener((obj, oldV, newV) -%3E redraw());%0A%09%7D%0A%0A%09@Override%0A%09public Affine getScale() %7B%0A%09%09return new Affine(new Scale(SCALE, SCALE
 ));%0A
@@ -1923,24 +1923,61 @@
 = MARGIN;%0A%09%09
+group.getChildren().clear();%0A%09%09group.
 getChildren(
@@ -2384,24 +2384,30 @@
 tName());%0A%09%09
+group.
 getChildren(
@@ -2920,16 +2920,22 @@
 ge);%0A%09%09%09
+group.
 getChild

implement zooming and panning