package fragmenter.util;

import Jama.Matrix;

import java.util.*;

import geometric.Triangle;

/**
* This class is a class for inner nodes used in some segmentation algorithms.
*/
public class InnerNode extends Node {

	int posOfCross = 0;

	public InnerNode(Matrix vertex, int position, boolean valid) {
		this.v = vertex;
		this.neighbours = new HashSet<Node>();
		this.position = position;
		this.positionIsValid = valid;
	}

	public InnerNode(Matrix vertex) {
		this(vertex, 0, false);
	}

	@Override
	public List<Node> getNeighbours() {
		if (this.neighbours.size() < 3) {
			List<Node> lst = new LinkedList<Node>();
			lst.addAll(this.neighbours);
			return lst;
		}

		Iterator<Node> it = this.neighbours.iterator();
		Node node1 = it.next();
		Node node2 = null;
		Queue<Matrix> queue = new PriorityQueue<Matrix>(5, new NormOneComparator());
		while (it.hasNext()) {
			node2 = it.next();
			Matrix m = Triangle.cross(node1.getVertex().minus(this.getVertex()), node2.getVertex().minus(
				this.getVertex()));
			queue.add(m);
		}
		this.posOfCross = this.getFirstNotNull(queue.peek());

		List<Node> lst = new LinkedList<Node>();
		lst.addAll(this.neighbours);
		Collections.sort(lst, new Comp(node1));

		lst.add(lst.get(0));

		return lst;
	}

	@Override
	protected double getAngle(Node node1, Node node2) {
		Matrix v1 = node1.getVertex().minus(this.getVertex());
		Matrix v2 = node2.getVertex().minus(this.getVertex());
		// calc scalar product
		Matrix v = v1.arrayTimes(v2);
		double scalar = v.get(0, 0) + v.get(1, 0) + v.get(2, 0);
		double angle = Math.acos(scalar / (v1.norm2() * v2.norm2()));
		Matrix cross = Triangle.cross(v1, v2);
		if (cross.get(this.posOfCross, 0) < 0) {
			angle = 2 * Math.PI - angle;
		}
		return angle;
	}

	private int getFirstNotNull(Matrix m) {
		if (m.get(0, 0) != 0) {
			return 0;
		}
		if (m.get(1, 0) != 0) {
			return 1;
		}
		return 2;
	}

	private class NormOneComparator implements Comparator<Matrix> {

		@Override
		public int compare(Matrix o1, Matrix o2) {
			double c1 = o1.norm1();
			double c2 = o2.norm1();
			return (c1 < c2) ? -1 : (c1 > c2) ? 1 : 0;
		}

	}
}
