package yi.graph.polygon;

import java.awt.*;
import java.awt.geom.*;
import yi.iu.processor.clc.*;
import yi.iu.processor.ort.*;
import jim.image.*;
import yi.graph.polygon.gpc.*;
import java.io.*;

/**
 * Generic Polygon
 */

public class GenericContour implements Serializable{

  /**
   * x of the first point will not repeated in the last spot.  In other words,
       * x.length =  number of vertices.  While for ConvexPolygon, the last point is
   * the same as the first one
   */
  public double[] x; // x of Vertices
  public double[] y; // y of Vertices

  public GenericContour() {
  }

  public GenericContour(double[] x, double[] y) {
    this.x = x;
    this.y = y;
  }

  public GenericContour(XPolygon poly) {
    x = new double[poly.npoints];
    y = new double[poly.npoints];
    for (int j = 0; j < poly.npoints; j++) {
      x[j] = poly.xpoints[j];
      y[j] = poly.ypoints[j];
    }
  }

  /**
   * Init a Generic polygon from a ConvexPolygon.   Remember that vertices in
   * ConvexPolygon is a closed contour in which the first point is the same as
   * the last; while there is not repeated first point in GPolygon point list
   * @param convex
   */
  public GenericContour(ConvexPolygon convex) {
    Vertexes vertices = convex.getVertexes();
    int N = (vertices == null) ? 0 : vertices.size();
    if (N > 0)
      N--; // adjust the number because there is not repeated first point in GPolygon
    x = new double[N];
    y = new double[N];
    for (int i = 0; i < N; i++) {
      Point p = (Point) vertices.elementAt(i);
      x[i] = p.x;
      y[i] = p.y;
    }
  }

  public GenericContour(GpcContour contour) {
    this(contour.x, contour.y);
  }

  public int getVertexNum() {
    return (x == null) ? 0 : x.length;
  }

  /**
   * Return the area of the polygon.  This method doesn't work if the polygon
   * is self intersecting
   * @return
   */
  public double getArea() {

    int N = getVertexNum();

    double area = 0;
    for (int i = 0; i < N; i++) {
      int j = (i + 1) % N;
      area += x[i] * y[j];
      area -= y[i] * x[j];
    }

    area /= 2.0;
    return (area < 0 ? -area : area);

  }

  public double getLeft() {
    double left = Double.POSITIVE_INFINITY;
    for (int i=0; i<x.length; i++) {
      if (x[i] < left) left = x[i];
    }
    return left;
  }

  public double getRight() {
    double right = Double.NEGATIVE_INFINITY;
    for (int i=0; i<x.length; i++) {
      if (x[i] > right) right = x[i];
    }
    return right;
  }

  public double getTop() {
    double top = Double.POSITIVE_INFINITY;
    for (int i=0; i<y.length; i++) {
      if (y[i] < top) top = y[i];
    }
    return top;
  }

  public double getBottom() {
    double bottom = Double.NEGATIVE_INFINITY;
    for (int i=0; i<y.length; i++) {
      if (y[i] > bottom) bottom = y[i];
    }
    return bottom;
  }

  public Point2D.Double getCentroid() {
    int N = getVertexNum();

    double cx = 0, cy = 0;
    double A = getArea();

    double factor = 0;
    for (int i = 0; i < N; i++) {
      int j = (i + 1) % N;
      factor = x[i] * y[j] - x[j] * y[i];
      cx += (x[i] + x[j]) * factor;
      cy += (y[i] + y[j]) * factor;
    }
    A *= 6.0;
    factor = 1 / A;
    cx *= factor;
    cy *= factor;
    return new Point2D.Double(cx, cy);
  }

  public void scale(double scale) {
    for (int i=0; i<x.length; i++) {
      x[i] *= scale;
      y[i] *= scale;
    }
  }

  public void shift(double dx, double dy) {
    for (int i=0; i<x.length; i++) {
      x[i] += dx;
      y[i] += dy;
    }
  }

  public String toString() {
    StringBuffer buf = new StringBuffer();

    int N = getVertexNum();

    buf.append("vertics = " + N);
    buf.append(" {");
    for (int i = 0; i < N; i++) {
      if (i != 0)
        buf.append(" ,");
      buf.append("(" + x[i] + ", " + y[i] + ")");
    }
    buf.append("}");

    return buf.toString();
  }

  public void draw(int[] pixels, int width, int height, int color, int thickness) {

    int n = getVertexNum();
    if (n<2) return;

    int delta_x = width / 2;
    int delta_y = height / 2;

    LineClusterSet bound_lines = new LineClusterSet();
    Point p1 = new Point( (int) Math.round(x[0]), (int) Math.round(y[0]));
    for (int j = 1; j < n; j++) {
      Point p2 = new Point( (int) Math.round(x[j]), (int) Math.round(y[j]));
      bound_lines.add(new ORTLine(p1.x - delta_x, p1.y - delta_y,
                                  p2.x - delta_x, p2.y - delta_y));
      p1 = p2;
    }
    if (n>2) {
      p1 = new Point( (int) Math.round(x[n-1]), (int) Math.round(y[n-1]));
      Point p2 = new Point( (int) Math.round(x[0]), (int) Math.round(y[0]));
      bound_lines.add(new ORTLine(p1.x - delta_x, p1.y - delta_y,
                                  p2.x - delta_x, p2.y - delta_y));
    }
    bound_lines.draw(pixels, width, height, color, thickness, 0, 0);

  }

  public XPolygon toXPolygon() {
    int n= getVertexNum();
    int[] intx = new int[n];
    int[] inty = new int[n];
    for (int i=0; i<n; i++) {
      intx[i] = (int)Math.round(x[i]);
      inty[i] = (int)Math.round(y[i]);
    }
    return new XPolygon(intx, inty, n);
  }


}