package fragmenter.util;

import Jama.Matrix;

import java.io.*;
import java.util.*;

import geometric.Triangle;

/**
* This class is an abstract class for nodes used in some segmentation algorithms.
*/
public abstract class Node {

	protected class Comp implements Comparator<Node> {

		private Node refPoint;

		public Comp(Node refPoint) {
			if (refPoint == null) {
				throw new NullPointerException("refPoint is null");
			}
			this.refPoint = refPoint;
		}

		@Override
		public int compare(Node o1, Node o2) {
			double angle1 = Node.this.getAngle(o1, this.refPoint);
			double angle2 = Node.this.getAngle(o2, this.refPoint);
			return (angle1 == angle2) ? 0 : (angle1 < angle2) ? -1 : 1;
		}

	}

	protected Matrix v;

	public abstract List<Node> getNeighbours();

	protected Set<Node> neighbours;
	protected int position;
	protected boolean positionIsValid = false;

	public static Matrix getCartesian(double phi, double theta) {
		double[][] cArr = new double[3][1];
		cArr[0][0] = Math.sin(theta) * Math.cos(phi);
		cArr[1][0] = Math.sin(theta) * Math.sin(phi);
		cArr[2][0] = Math.cos(theta);

		return new Matrix(cArr);
	}

	public int getPosition() {
		return this.position;
	}

	public void setPosition(int position) {
		this.position = position;
		this.positionIsValid = true;
	}

	public boolean isPositionIsValid() {
		return this.positionIsValid;
	}

	public boolean addNeighbour(Node neighbour) {
		if (this.v.minus(neighbour.getVertex()).norm2() > Triangle.EPS) {
			return this.neighbours.add(neighbour);
		} else {
			// node is supposed to be his own neighbour -> deny
			return false;
		}
	}

	protected abstract double getAngle(Node node1, Node node2);

	public void removeNeighbour(Node neighbour) {
		this.neighbours.remove(neighbour);

	}

	public Matrix getVertex() {
		return this.v;
	}

	@Override
	public String toString() {
		Matrix printVertex = this.getVertex().copy();
		for (int i = 0; i < 3; i++) {
			if (Math.abs(printVertex.get(i, 0)) < Triangle.EPS) {
				printVertex.set(i, 0, 0);
			}
		}
		StringWriter sw = new StringWriter();
		PrintWriter pw = new PrintWriter(sw);
		printVertex.print(pw, 2, 10);
		return sw.toString();
	}

	public Node() {
		super();
	}

	public double getTheta() {
		double x = this.getVertex().get(0, 0);
		double y = this.getVertex().get(1, 0);
		double z = this.getVertex().get(2, 0);

		return (Math.PI / 2) - Math.atan(z / (Math.sqrt(x * x + y * y)));
	}

	public double getPhi() {
		double x = this.getVertex().get(0, 0);
		double y = this.getVertex().get(1, 0);
		double p = Math.acos(x / (Math.sqrt(x * x + y * y)));
		if (y >= 0) {
			return p;
		} else {
			return (2 * Math.PI) - p;
		}
	}
}