// $Id: PolyGroup.java,v 1.1 2003/06/03 15:37:14 cadlerun Exp $
// PolyGroup.java -- 
// Author: Aaron Adler <cadlerun@fracas.ai.mit.edu> 
// Copyright: Copyright (C) 2003 by MIT.  All rights reserved.
// Created: <Tue Apr 15 11:14:18 2003> 

package rec.multi;

import edu.mit.tools.watch.Logging;
import org.apache.log4j.Logger;

import rec.geo.Geometry;
import rec.geo.GPoint;
import rec.geo.GPolygon;
import rec.simulate.widgets.WSimPolygon;
  
import java.util.ArrayList;
import java.util.Iterator;

import java.awt.Rectangle;

/***
 * This is a static class that takes a list of polygon widgets and
 * makes them identical if they have the same number of sides.
 ***/

public class PolyGroup {

  private static final Logger LOG = Logging.SPEECH;
  
  public static void makeIdentical (ArrayList listOfPolyWidgets) {
    Iterator widgetIterator = listOfPolyWidgets.iterator();
    int corners = -1;
    ArrayList origPolys = new ArrayList();
    ArrayList polys = new ArrayList();
    ArrayList startCorner = new ArrayList();
    ArrayList drawnClockwise = new ArrayList();
    
    while (widgetIterator.hasNext()) {
      WSimPolygon polyW = (WSimPolygon) widgetIterator.next();
      GPolygon origPoly = (GPolygon)polyW.getShape();
      GPolygon poly = (GPolygon)origPoly.copy();
      int pCorners = poly.getNumberOfSides();
      if (corners == -1) {
	// first poly
	corners = pCorners;
      }
      if (pCorners != corners) {
	LOG.error("Not all polys had same number of sides");
	return; // not all polys had same # of sides
      }
      // now translate the poly to 0, 0
      // get the center point
      Rectangle bounds = poly.getBounds();
      double centerX = (bounds.getX() + (bounds.getWidth() / 2.0));
      double centerY = (bounds.getY() + (bounds.getHeight() / 2.0));
      poly.translate(-centerX, -centerY);

      // add the poly to the list
      polys.add(poly);
      origPolys.add(origPoly);
      drawnClockwise.add(isDrawnClockwise(origPoly));

      // find upper left corner
      bounds = poly.getBounds();
      GPoint topLeft = new GPoint(bounds.getX(), bounds.getY());

      // cycle through the polygon and find the point closest to this corner
      // we want this index so we can compare the points of the different
      // polygons
      double shortestDist = Double.MAX_VALUE;
      int closestCorner = -1;
      GPoint[] points = poly.getPoints();

      for (int i = 0; i < corners; i++) {
	double dist = Geometry.distance(topLeft, points[i]);
	if (dist < shortestDist) {
	  // closer corner
	  closestCorner = i;
	  shortestDist = dist;
	}
      }
      LOG.info("closest corner is "+closestCorner);
      startCorner.add(new Integer(closestCorner));
    }

    // now we need to find the average of the polygons

    ArrayList xPoints = new ArrayList();
    ArrayList yPoints = new ArrayList();
    // initialize the arrays
    for (int i = 0; i < corners; i++) {
      xPoints.add(new Double(0));
      yPoints.add(new Double(0));
    }
    
    for (int i = 0; i < polys.size(); i++) {
      // calculate the average for the current point
      GPoint[] points = ((GPolygon)polys.get(i)).getPoints();
      int start = ((Integer)startCorner.get(i)).intValue();

      // we need to loop in a different direction for clockwise and counter-clockwise
      if (((Boolean)drawnClockwise.get(i)).booleanValue() == true) {
	// clockwise
	for (int j = 0; j < points.length; j++) {
	  LOG.debug("points "+points.length);
	  LOG.debug("start + j "+(start+j));
	  LOG.debug("mod "+((start+j) % points.length));
	  int index = (start + j) % points.length;
	  GPoint pt = points[index];
	  double x = pt.getX();
	  double y = pt.getY();
	  double xVal = ((Double)xPoints.get(j)).doubleValue();
	  double yVal = ((Double)yPoints.get(j)).doubleValue();
	  xPoints.set(j, new Double(xVal + x));
	  yPoints.set(j, new Double(yVal + y));
	}
      }
      else {
	// counter-clockwise
	for (int j = 0; j < points.length; j++) {
	  LOG.debug("points "+points.length);
	  LOG.debug("start - j "+(start-j));
	  LOG.debug("mod "+((start-j+points.length) % points.length));
	  int index = (start - j + points.length) % points.length;
	  GPoint pt = points[index];
	  double x = pt.getX();
	  double y = pt.getY();
	  double xVal = ((Double)xPoints.get(j)).doubleValue();
	  double yVal = ((Double)yPoints.get(j)).doubleValue();
	  xPoints.set(j, new Double(xVal + x));
	  yPoints.set(j, new Double(yVal + y));
	}
	
      }
    }

    // now divide to get the average
    for (int i = 0; i < corners; i++) {
      xPoints.set(i, new Double(((Double)xPoints.get(i)).doubleValue() / (double)polys.size()));
      yPoints.set(i, new Double(((Double)yPoints.get(i)).doubleValue() / (double)polys.size()));
    }

    // now we need to shift the points on the actual polygons
    for (int i = 0; i < origPolys.size(); i++) {
      GPolygon p = (GPolygon)origPolys.get(i);
      GPoint[] pts = p.getPoints();
      int start = ((Integer)startCorner.get(i)).intValue();

      Rectangle bounds = p.getBounds();
      double centerX = (bounds.getX() + (bounds.getWidth() / 2.0));
      double centerY = (bounds.getY() + (bounds.getHeight() / 2.0));
      LOG.debug("center: "+centerX+" "+centerY);
      
      // we need to loop in a different direction for clockwise and counter-clockwise
      if (((Boolean)drawnClockwise.get(i)).booleanValue() == true) {
	// clockwise
	for (int j = 0; j < pts.length; j++) {
	  int index = (start + j) % pts.length;
	  GPoint pt = pts[index];
	  double avgX = ((Double)xPoints.get(j)).doubleValue();
	  double avgY = ((Double)yPoints.get(j)).doubleValue();
	  LOG.debug(" x avg "+avgX+" y avg "+avgY);
	  double newX = centerX + avgX;
	  double newY = centerY + avgY;
	  LOG.debug("  x new "+newX+" y new "+newY);
	  double shiftX = newX - pt.getX();
	  double shiftY = newY - pt.getY();
	  LOG.debug("   x shift "+shiftX+" y shift "+shiftY);
	  pt.translate(shiftX, shiftY);
	}
      }
      else {
	// counter-clockwise
	for (int j = 0; j < pts.length; j++) {
	  int index = (start - j + pts.length) % pts.length;
	  GPoint pt = pts[index];
	  double avgX = ((Double)xPoints.get(j)).doubleValue();
	  double avgY = ((Double)yPoints.get(j)).doubleValue();
	  LOG.debug(" x avg "+avgX+" y avg "+avgY);
	  double newX = centerX + avgX;
	  double newY = centerY + avgY;
	  LOG.debug("  x new "+newX+" y new "+newY);
	  double shiftX = newX - pt.getX();
	  double shiftY = newY - pt.getY();
	  LOG.debug("   x shift "+shiftX+" y shift "+shiftY);
	  pt.translate(shiftX, shiftY);
	}

      }

    }

    //redraw widgets
    widgetIterator = listOfPolyWidgets.iterator();
    if (widgetIterator.hasNext()) {
      WSimPolygon polyW = (WSimPolygon) widgetIterator.next();
      polyW.getSurface().getScreen().repaint();
    }
  }

  ///////////////////////////


  private static Boolean isDrawnClockwise(GPolygon poly) {
    GPoint[] pts = poly.getPoints();
    Rectangle bounds = poly.getBounds();
    double centerX = (bounds.getX() + (bounds.getWidth() / 2.0));
    double centerY = (bounds.getY() + (bounds.getHeight() / 2.0));
    GPoint center = new GPoint(centerX, centerY);
    
    double angle = 0.0; // angle in radians

    for (int i = 0; i < pts.length; i++) {
      GPoint ptA = pts[i];
      GPoint ptB = pts[((i + 1) % pts.length)];

      double a = Geometry.angleBetween(ptA, center, ptB);
      // make angle between -pi and +pi
      if (a > Math.PI) {
	a = a - (2.0 * Math.PI);
      }
      //LOG.debug("angle is "+a);
      angle += a;
    }

    LOG.debug("angle total is "+angle);

    // the angle total will be +2pi if it clockwise and -2pi if it is counter clockwise
    // if angle is greater than 0 return true, otherwise return false
    if (angle > 0.0) {
      return new Boolean(true);
    }
    else {
      return new Boolean(false);
    }
  }
}
