import java.awt.geom.Path2D;
import java.awt.geom.AffineTransform;
import java.awt.Graphics2D;
import java.awt.geom.Line2D;
import java.awt.*;
/**
 *
 * @author Felipe Lopez P. - Cristian Carrasco G.
 * Class GRubberBand: esta clase modela graficamente un elástico. Para
 * realizar esto se hereda de la clase RubberBand y se implemeta la clase Draggable.
 */
public class GRubberBand extends RubberBand implements Draggable {
   private static final double xPoints[]={0,0.025,0.05,0.075,0.01, 0.125, 0.175, 0.225, 0.275, 0.325,
                                          0.375, 0.425, 0.475, 0.525,0.575,0.625,
                                          0.675,0.725,0.775,0.825,0.875, 0.90,1.0};
   private static final Path2D.Double polyline =
                      new Path2D.Double(Path2D.WIND_EVEN_ODD,xPoints.length);
   private Path2D.Double shape;
   private Color color = Color.BLACK;
   private Vector2D graphicsPosA =null;  // to draw a spring we need
   private Vector2D graphicsPosB =null;  // extremes well definied
                                      // when it is not connected

   static {  // static initialization block
      polyline.moveTo (xPoints[0], -0.1*xPoints[0]*(xPoints[0]-1));
      for (int index = 1; index < xPoints.length;index++)
         polyline.lineTo(xPoints[index], -0.1*xPoints[index]*(xPoints[index]-1));
   }
/**
 * Constructor del elástico graficamente.
 * @param restLength Se ingresa el largo con el cual se desea crear el elastico.
 * @param stiffness Se ingresa la constante elástica del elastico.
 */
   public GRubberBand(float restLength, float stiffness) {
      super(restLength,stiffness);
      graphicsPosA = new Vector2D(0,restLength);
      graphicsPosB = new Vector2D(restLength,restLength);
      AffineTransform at = AffineTransform.getTranslateInstance(0,0);
      Vector2D v = getVector();
      at.rotate(v.getX(), v.getY());
      at.scale(v.module(), restLength*restLength/v.module());
      shape = (Path2D.Double) at.createTransformedShape(polyline);
   }
/**
 * Se dibuja el elastico.
 * @param g Objeto Graphics2D.
 */
  public void draw (Graphics2D g){
      Vector2D a=getPositionA();
      double ax = a.getX();
      double ay = a.getY();
      Vector2D v = getVector();
      AffineTransform at = AffineTransform.getTranslateInstance(ax, ay);
      at.rotate(v.getX(), v.getY());
      at.scale(v.module(), restLength*restLength/v.module());
      shape = (Path2D.Double) at.createTransformedShape(polyline);

      g.setColor(color);
      g.draw(shape);
   }

   public boolean contains(double x, double y){
      return shape.contains(x,y);
   }
   /**
    * Arrastre del elastico.
    * @param x Nueva posicion x.
    * @param y Nueva posicion y.
    */
   public void drag(double x, double y){
      Vector2D a=getPositionA();
      Vector2D b=getPositionB();
      double da = Math.abs(a.getX()-x)+Math.abs(a.getY()-y); // distance to a
      double db = Math.abs(b.getX()-x)+Math.abs(b.getY()-y); // distance to b
      if (da < db) {
         detachBlock_a();
         graphicsPosA.moveTo(x,y);
      }
      else {
         detachBlock_b();
         graphicsPosB.moveTo(x,y);
      }
   }
   /**
    * Posicion "A" del elástico.
    * @return Posicion "A" del elástico, en un Vector2D.
    */
   public Vector2D getPositionA() {
      Vector2D p = super.getPositionA();
      if (p != null)
         return p;
      else
         return graphicsPosA;
   }
    /**
    * Posicion "B" del elástico.
    * @return Posicion "B" del elástico, en un Vector2D.
    */
   public Vector2D getPositionB() {
      Vector2D p = super.getPositionB();
      if (p != null)
         return p;
      else
         return graphicsPosB;
   }
/**
 * Actualizacion del estado.
 * Este metodo es similar al del resorte, se modifico para que cambie el color
 * cuando el elástico se encuentre en tension y distension.
 */
  public void updateState(){
      double largo = (getPositionA().minus(getPositionB())).module();
      if(largo > restLength) setColorTension();
      if(largo < restLength) setColorDistension();

      if (block_a==null && block_b==null) return;
      if (block_a!=null) {
         Vector2D v = getVector().unitary().times(restLength);
         graphicsPosB = getPositionA().plus(v);
         return;
      }
      if (block_b!=null) {
         Vector2D v = getVector().unitary().times(restLength);
         graphicsPosA = getPositionB().minus(v);
         return;
      }
   }
   public void attachNearElement(double x, double y, BlockSpringConfiguration bsc){
      Vector2D a=getPositionA();
      if ((a.getX()==x) && (a.getY()==y)) {
          GBlock block = bsc.findBlock(x,y);
          if (block!=null)attachBlock_a(block);
         else
         return;
      }
      Vector2D b=getPositionB();
      if ((b.getX()==x) && (b.getY()==y))  {
         GBlock block = bsc.findBlock(x,y);
         if (block!=null)attachBlock_b(block);
         return;
      }
   }
   /**
    * Seteo del color cuando se selecciona el elástico.
    */
   public void setSelectedColor() {
      color = Color.RED;
   }
   /**
    * Seteo del color por defecto.
    */
   public void setDefaultColor() {
      color = Color.BLACK;
   }
   /**
    * Seteo del color cuando el elástico esta en tension.
    */
   public void setColorTension() {
      color = Color.BLUE;
   }
   /**
    * Seteo del color cuando el elástico esta en distension.
    */
   public void setColorDistension() {
      color = Color.GREEN;
   }

}
