package ProGAL.geom2d;

import ProGAL.dataStructures.*;
import ProGAL.geom2d.viewer.J2DScene;


public class ConvexPolygon extends Polygon{
	private static final long serialVersionUID = 1L;

	public ConvexPolygon() { super(); }

	/** creates a convex hull of a simple polygon in O(n) time */
	public ConvexPolygon(Polygon pol) {
		int i = pol.leftExtremePointIndx();
		int n = pol.size();
		int k = (i+n-1)%n;
		add(pol.get(k));
		add(pol.get(i));
		add(pol.get((i+1)%n));
		int j = (i+2)%n;
		while (j != k) {
			Point p = pol.get(j);
			if (contains(p)) j++;
			else while (!Point.rightTurn(p, get(size()-1), get(size()-2))) deleteLast();
			add(p);
			j = (j+1)%n;
		}
	}

		public static enum ConvexHullAlgorithm  { JarvisMarch, GrahamsScan };
	
	/** creates a convex polygon with given three points as corners (counterclockwise) */
	public ConvexPolygon(Point p0, Point p1, Point p2) { super(p0, p1, p2); }
	
	/** creates a convex hull of a set of points 
	 * Jarvis March O(hn)
	 * Grahams Scan O(nlogn)  */
	public ConvexPolygon(PointSet points, ConvexHullAlgorithm algorithm) {
		switch (algorithm) {
		case JarvisMarch:
			Point p = points.leftExtremePoint();
			Point r = p;
			do add(r = points.getNextExtremePoint(r)); while (r != p);
			break;
		case GrahamsScan:
			Sorter sort = new SorterQuick();
			sort.Sort(points, new SortToolPoint2dAroundPoint(points.getCentroid()), false);
			Polygon pol = new Polygon(points);   
			int indx = pol.leftExtremePointIndx();
			int k1 = (indx + pol.size() - 1)%pol.size();
			int k2 = indx;
			int k3 = (indx + 1)%pol.size();
			boolean back = true;
			while ((k2 != indx) || back) {
				if (Point.leftTurn(pol.get(k1), pol.get(k2), pol.get(k3))) {
					k1 = k2;
					k2 = k3;
					k3 = (k2 + 1)%pol.size();
					back = false;
				}
				else {
					pol.remove(k2); 
					if (k2 < indx) indx--;  // 012  123   234  345  450 501       401 012 123 234 340 340
					if (k1 == 0) k1 = pol.size()-1; else { if (k1 == pol.size()) k1 = k1-2; else k1--; }
					k2 = (k1 + 1)%pol.size();
					k3 = (k2 + 1)%pol.size();
					back = true;
				}
			}
			for (Point q : pol) add(q);
			break;
		}
	}
	

	
	/** returns true if point p is inside the convex polygon */
	public boolean contains(Point p) {
		for (int i = 0; i < size()-1; i++) if (Point.rightTurn(get(i), get(i+1), p)) return false; 
		return Point.leftTurn(get(size()-1), get(0), p);
	}
	
	
	/** returns the segment between most distant pair of points. */
	public LineSegment getDiameter() {
		int n = size();
		int i = leftExtremePointIndx();  
		int iStart = i;
		int iNext = (i + 1 == n)? 0 : i+1;
		Vector vi = new Vector(get(i), get(iNext));
		int j = rightExtremePointIndx(); 
		int jStart = j;
		int jNext = (j + 1 == n)? 0 : j+1;
		Vector vj = new Vector(get(jNext), get(j));
		LineSegment seg = new LineSegment(get(i), get(j));
		LineSegment diameter = seg.clone();
		double diamLength = seg.getSquaredLength();
		double lng;
		do {
//			System.out.println(i + ": " + (get(i)).toString() + ",  " + j +": " + (get(j)).toString() + ",  " + seg.getSquaredLength());
//			System.out.println("Vectors vi and vj: " + vi.toString() + "  " + vj.toString());
			if  (Vector.rightTurn(vi, vj)) {
				j = jNext;
				jNext = (j + 1 == n)? 0 : j+1;
				vj = new Vector(get(jNext), get(j));
				seg.setB(get(j));
			}
			else {
				i = iNext;
				iNext = (i + 1 == n)? 0 : i+1;
				vi = new Vector(get(i), get(iNext));
				seg.setA(get(i));
			}
			lng = seg.getSquaredLength();
			if (lng > diamLength) { diameter = seg.clone(); diamLength = lng; }
		} while  ((i != jStart) || (j != iStart)) ;
		return diameter;
	}

	public double[][] beamDetector() {
		int n = size();
		double[][] bp = new double[n][n];
		int[][] p = new int[n][n];
		for (int k = 0; k < n; k++) { bp[k][(k+1)%n] = 0.0; p[k][(k+1)%n] = k; }
		for (int l = 2; l < n; l++) {
			for (int i = 0; i < n; i++) {
				int j = (i+l)%n;
				LineSegment seg = new LineSegment(get(i), get(j));
				bp[i][j] = 99999999.9;
				int k = i;
				while (k != j) {
					k = (k+1)%n;
					double length = bp[i][k] + bp[k][j] + get(k).getDistance(seg);
					if (bp[i][j] > length) {
						p[i][j] = k;
						bp[i][j] = length;
					}
				}
			}
		}
		return bp;
	}
	
	public int farthestVertex(int p, int r) {
		int n = size();
		LineSegment seg = new LineSegment(get(p), get(r));
		double dist = 0.0;
		int q = -1;
		int i = (p+1)%n; 
		while (i != r) {
			double d = get(i).getSquaredDistance(seg);
			if (d > dist) {
				dist = d;
				q = i;
			}
			i = (i+1)%n;
		}
		return q;
	}
	


}
