import java.awt.Graphics;
import java.awt.*;
import java.util.*;

public class MyPolygon
{
	Point pts[];
	int num_pts;
	DeflationApplet applet;

	/*
	    Initialize a polygon with num points.
	*/

	public MyPolygon(int num, DeflationApplet _d)
	{
		int i;
		Random r = new Random();
		applet = _d;
		if(num == -1){
			num = (int) Math.floor(r.nextDouble() * 10);
			num *= 4;
			num -= (Math.floor(r.nextDouble()) % 4);
		}
		pts = new Point[num];
		num_pts = num;
		for(i = 0; i < num_pts; i++){
			pts[i] = new Point();
		}
			
	}

	public void makeRandomPoly(int width, int height)
	{
		double greatest_x, least_x, least_y;
		int greatest_pt = 0;
		int i;
		Vector v;
		Random r = new Random();

		for(i = 0; i < num_pts; i++){
			pts[i].x = r.nextDouble() * width;
			pts[i].y = r.nextDouble() * height;
		}
		greatest_x = pts[0].x;
		least_x = pts[0].x;
		least_y = pts[0].y;
		for(i = 0; i < num_pts; i++){
			if(pts[i].x < least_x){
				least_x = pts[i].x;
				least_y = pts[i].y;
			}
			if(pts[i].x > greatest_x){
				greatest_x = pts[i].x;
				greatest_pt = i;
			}
		}
		pts[greatest_pt].y = least_y;
		Point.splitting_y = least_y;
		Point.sort(pts, new PointComparer());
	}


	/*  
	    This algorithm seems to work. All it really does is compute
	    the point on the line from p1 to p2 that is closest to p[i].
	    Then we find the difference between p[i] and that point.  We
	    then add this difference to the previously computed point,
	    giving this value to p[i].
	*/

	public void reflect2Points(int p1, int p2, boolean poly_is_random)
	{
		double r_sq;
		double x1, x2, x3, y1, y2, y3;
		int i;
		double len1, len2;

		if(!poly_is_random){
			if(!cutIsValid(p1, p2)){
				return;
			}
		} else {
			if(!cutIsValidRandom(p1, p2)){
				return;
			}
		}
		// This is a little hack to make the shorter segments be
		// reflected
		
		i = p1;
		len1 = 0.0;
		while(i != p2){
			len1 += edgeLength(i, (i + 1) % num_pts);
			i++;
			i %= num_pts;
		}
		i = p1;
		len2 = 0.0;
		while(i != p2){
			if(i != 0)
				len2 += edgeLength(i, (i - 1) % num_pts);
			else
				len2 += edgeLength(i, num_pts - 1);
			i--;
			if(i < 0) i = num_pts - 1;
		}
		// if len1 is greater, then switch the cut
		if(len1 > len2){
			int temp = p1;
			p1 = p2;
			p2 = temp;
		}

		// the length of the line segment squared
		r_sq = (pts[p2].x - pts[p1].x) * (pts[p2].x - pts[p1].x) + (pts[p2].y - pts[p1].y) * (pts[p2].y - pts[p1].y);
		x1 = pts[p1].x;
		x2 = pts[p2].x;
		y1 = pts[p1].y;
		y2 = pts[p2].y;

		i = p1 + 1;
		i %= num_pts;

		while(i != p2){
			double x, y, xdiff, ydiff, realx, realy;
			double u;
			x3 = pts[i].x;
			y3 = pts[i].y;
			u = ((x3 - x1) * (x2 - x1) + (y3 - y1) * (y2 - y1)) / r_sq;
			x = x1 + u * (x2 - x1);
			y = y1 + u * (y2 - y1);
			xdiff = x3 - x;
			ydiff = y3 - y;
			realx = x - xdiff;
			realy = y - ydiff;
			pts[i].x = realx;
			pts[i].y = realy;
			i++;
			i %= num_pts;
		}
	}

	public boolean cutIsValidRandom(int p1, int p2)
	{
		int i;
		int regular_poly[];
		int poly_1[];
		int poly_2[];
		int count;

		if(!isSimple())
			return true;

		for(i = 0; i < num_pts; i++){
			if(intersect(p1, p2, i, (i + 1) % num_pts)){
				return false;
			}
		}

		if(p2 < p1){ // the rest of the code depends on p1 < p2
			int temp = p1;
			p1 = p2;
			p2 = temp;
		}

		regular_poly = new int[num_pts];
		for(i = 0; i < num_pts; i++){
			regular_poly[i] = i;
		}
		count = 0;
		i = p1;
		while(i != p2){
			i++;
			i %= num_pts;
			count++;
		}
		poly_1 = new int[count + 1];
		count = 0;
		i = p1;
		while(i != p2){
			poly_1[count] = i;
			i++;
			i %= num_pts;
			count++;
		}
		poly_1[count] = i;

		count = 0;
		i = p1;
		while(i != p2){
			i--;
			i += num_pts;
			i %= num_pts;
			count++;
		}

		poly_2 = new int[count + 1];
		count = 0;
		i = p1;
		while(i != p2){
			poly_2[count] = i;
			i--;
			i += num_pts;
			i %= num_pts;
			count++;
		}
		poly_2[count] = i;

		if(Math.abs(Math.abs(area_poly(regular_poly)) - (Math.abs(area_poly(poly_1)) + Math.abs(area_poly(poly_2)))) > 1e-8){
			applet.showStatus("not a valid cut");
			return false;
		}
		applet.showStatus("valid cut");
		return true;
	}


	// Returns true if the cut is valid.  That is, it is 
	// not a line of support and it does not intersect the
	// polygon
	//
	// This only seems to work for quadrilaterals

	public boolean cutIsValid(int p1, int p2)
	{
		double dir;
		// These variables will be positive or negative
		// depending on whether they are a right turn from the cut
		// or not
		double predir = 0.0; // for points > p1 and < p2
		double postdir = 0.0; // for points > p2 and < p1
		int i;

		if(p2 < p1){ // the rest of the code depends on p1 < p2
			int temp = p1;
			p1 = p2;
			p2 = temp;
		}

		for(i = 0; i < num_pts; i++){
			if((i == p1) || (i == p2))
				continue;
			dir = area(p1, p2, i); 
			if(i > p1 && i < p2){
				if(predir == 0){
					// set the direction if it hasn't been set before
					predir = dir;
					// these catch intersections with the polygon
				} else if(predir < 0){
					if(dir > 0){
						return false;
					}
				} else if(predir > 0) {
					if(dir < 0){
						return false;
					}
				}
			} else if(i > p2 || i < p1) { 
				if(postdir == 0){
					postdir = dir;
				} else if(postdir < 0){
					if(dir > 0){
						return false;
					}
				} else if(postdir > 0){
					if(dir < 0){
						return false;
					}
				}
			}
		}

		// These catch lines of support
		if(postdir < 0 && predir < 0)
			return false;
		if(postdir > 0 && predir > 0)
			return false;

		return true;
	}

	public boolean isSimple()
	{
		// UGH! this is an n^2 algorithm
		// There is the O(n) unprogrammable algorithm 
		// by Chazelle, as well as the O(nlog^*n) one
		// by Siedel which is implementable in "around
		// 700 lines of C++".
		// With a decent sorting algorithm, I could put
		// an O(n log n) algorithm in its place, but for right
		// now, I'll leave it.
		int i, j;
		
		for(i = 0; i < num_pts; i++){
			for(j = 0; j < num_pts; j++){
				if(intersect(i, (i + 1) % num_pts, j, (j + 1) % num_pts)){
					return false;
				}
			}
		}
		return true;
	}

	double area_poly(int p[])
	{
		int i;
		double sum = 0.0;
		for(i = 1; i < p.length - 1; i++){
			sum += area(p[0], p[i], p[i + 1]);
		}
		return sum;
	}

	double area(int p1, int p2, int p3)
	{
		double a;
		a = ((pts[p2].x - pts[p1].x) * (pts[p3].y - pts[p1].y)) - 
			((pts[p3].x - pts[p1].x) * (pts[p2].y - pts[p1].y));
		return a;
	}

	boolean left(int p1, int p2, int p3)
	{
		double a;
		a = area(p1, p2, p3);
		return a > 0;
	}

	boolean collinear(int p1, int p2, int p3)
	{
		double a;
		a = area(p1, p2, p3);
		return (Math.abs(a) < 1e-9);
	}

	boolean intersect(int p1, int p2, int q1, int q2)
	{
		boolean l1, l2, l3, l4;

		if((p1 == q1) || (p2 == q1) || (p1 == q2) || (p2 == q2))
			return false;

		//if(collinear(p1, p2, q1) || collinear(p1, p2, q2) || collinear(q1, q2, p1) || collinear(q1, q2, p2))
		//return false;

		l1 = left(p1, p2, q1);
		l2 = left(p1, p2, q2);
		l3 = left(q1, q2, p1);
		l4 = left(q1, q2, p2);

		return (l1 ^ l2) && (l3 ^ l4);
	}

	int nearPoint(int x, int y)
	{
		int i;
		for(i = 0; i < num_pts; i++){
			if(Math.abs(x - pts[i].X) < 5 && Math.abs(y - pts[i].Y) < 5)
				return i;
		}
		return -1;
	}

	public void scaleToSize(int x, int y)
	{
		int i;
		for(i = 0; i < num_pts; i++){
			pts[i].scaleToSize(x, y);
		}
	}

	public double edgeLength(int p1, int p2)
	{
		double xdiff = pts[p1].x - pts[p2].x;
		double ydiff = pts[p1].y - pts[p2].y;
		return Math.sqrt(xdiff * xdiff + ydiff * ydiff);
	}

	/*
	    Draw the polygon in red.
	*/

	void draw(Graphics g)
	{
		int i;
		int poly_x[] = new int[num_pts];
		int poly_y[] = new int[num_pts];

		g.setColor(Color.red);
		scaleToSize(200, 200);

		for(i = 0; i < num_pts; i++){
			poly_x[i] = pts[i].X;
			poly_y[i] = pts[i].Y;
		}
		g.drawPolygon(poly_x, poly_y, num_pts);
	}
}


syntax highlighted by Code2HTML, v. 0.9