package math.geometry;

import java.util.ArrayList;
import java.util.Arrays;

import math.equation.Polynomial;
import math.matrix.Vector2D;

public class Polygon {
	
	private int points;
	protected ArrayList<Point2D> polygon = new ArrayList<Point2D>(points);
	
	/**
	 * Create empty tetragon.
	 */
	public Polygon(int points){
		this.points = points;
		int i;
		for(i=0; i<points; i++)
			polygon.add(i, new Point2D(0,0));
	}
	
	/**
	 * Create tetragon by points.
	 * @param p points
	 */
	public Polygon(Point2D... p){
		this.points = p.length;
		int i;
		for(i=0; i<points; i++)
			polygon.add(i, p[i]);
	}
	
	/**
	 * Create tetragon by points.
	 * @param polygon 2D points
	 */
	public Polygon(ArrayList<Point2D> polygon){
		this.points = polygon.size();
		int i;
		for(i=0; i<points; i++)
			this.polygon.add(i, polygon.get(i));
	}

	
	/**
	 * Copy constructor
	 * @param polygon Polygon object
	 */
	public Polygon(Polygon polygon){
		points = polygon.points;
		for(Point2D p : polygon.get())
			this.polygon.add(new Point2D(p));
	}
	
	/**
	 * Get points of polygon.
	 * @return points
	 */
	public ArrayList<Point2D> get(){
		return polygon;
	}
	
	/**
	 * Get point of polygon.
	 * @param i index of polygon
	 * @return point
	 */
	public Point2D get(int i){
		return polygon.get(i);
	}
	
	/**
	 * Set point of tetragon.
	 * @param i index of point (first: 0)
	 * @param a location / point
	 * @return modified tetragon
	 */
	public Polygon set(int i, Point2D a){
		if(0<= i && i <points)
			polygon.set(i, a);
		return this;
	}
	
	/**
	 * Displace point from tetragon.
	 * @param i index of point (first: 0)
	 * @param a displacement vector
	 * @return tetragon with moved point
	 */
	private Polygon displaceIntern(int i, Vector2D a){
		polygon.set(i, (Point2D) polygon.get(i).displace(a));
		return this;
	}

	/**
	 * Displace point from tetragon.
	 * @param i index of point (first: 0)
	 * @param a displacement vector
	 * @return tetragon with moved point
	 */
	public Polygon displace(int i, Vector2D a){
		if(1< i && i <points)
			displaceIntern(i, a);
		return this;
	}
	
	/**
	 * Displace tetragon.
	 * @param a displacement vector
	 * @return moved tetragon
	 */
	public Polygon displace(Vector2D a){
		int i;
		for(i=0; i<points; i++)
			displaceIntern(i, a);
		return this;
	}

	public double perimeter(){
		int i;
		double perimeter = 0;
		for(i=0; i<points; i++)
			perimeter += get(i%4).distance(get((i+1)%4));
		return perimeter;
	}

	public double semiperimeter(){
		return perimeter()/2.;
	}
	
	public static Polynomial line(Point2D p1, Point2D p2){
		return new Polynomial(p1, p2);
	}
	
	/**
	 * Integrate discrete polygon curve.
	 * @return area
	 */
	public double integrate(){
		return integrate(false);
	}
	
	/**
	 * Integrate discrete polygon curve.
	 * @return area
	 */
	public double integrate(boolean stepByStep){
		int i, ii;
		Polynomial[] f = new Polynomial[points];
		double[] A = new double[points];
		double Ages = 0;
		for(i=0; i<points; i++){
			ii = (i+1)%points;
			if(get(i).x() != get(ii).x()){
				f[i] = line(get(i), get(ii));
				A[i] = f[i].integrate(get(i).x(), get(ii).x());
				Ages += A[i];
//				System.out.println("f" + i + "" + ii + " = " + f[i]);
			}
		}
		if(stepByStep) System.out.println("Ai = " + Arrays.toString(A));
		return Math.abs(Ages);
	}
	
	/**
	 * Area of polygon.
	 * @return area
	 */
	public double area(){
		return integrate();
	}
	
	/**
	 * Area of polygon.
	 * @return area
	 */
	public double area(boolean stepByStep){
		return integrate();
	}
	
	public boolean contains(Point2D p){
		boolean result = false;
		for(Point2D pi : get())
			if(pi.equals(p)) return true;
		return result;
	}
	
	public boolean pointInsideOuterRange(Point2D p){
		if(p.x() > minX() && p.x() < maxX() && p.y() > minY() && p.y() < maxY())
			return true;
		return false;
	}
	
	public double minX(){
		double min = Double.MAX_VALUE;
		for(Point p : polygon)
			if(p.x() < min) min = p.x();
		return min;
	}
	
	public double minY(){
		double min = Double.MAX_VALUE;
		for(Point p : polygon)
			if(p.y() < min) min = p.y();
		return min;
	}
	
	public double maxX(){
		double max = Double.MIN_VALUE;
		for(Point p : polygon)
			if(p.x() > max) max = p.x();
		return max;
	}
	
	public double maxY(){
		double max = Double.MIN_VALUE;
		for(Point p : polygon)
			if(p.y() > max) max = p.y();
		return max;
	}
	
	/**
	 * Text printout of polygon.
	 * <pre>{
	 *    p1(x1;y1)
	 *    p2(x2;y2)
	 *    ...
	 *    pn(xn;yn)
	 * }</pre>
	 */
	@Override
	public String toString(){
		int i;
		StringBuffer output = new StringBuffer();
		output.append("{\n");
		for(i=0; i<points; i++)
			output.append("   p"+(i+1)+get(i)+"\n");
		output.append("}");
		return new String(output);
	}
	
}
