import java.awt.event.*;
import java.awt.*;
import java.lang.Math;

/**
* <strong> Clase que implementa el ActionListener para
* la Rotación de Figuras Geométricas.<strong>
*/
class RotationHandler implements ActionListener
{
  private Dibujo dibujo;
  
/**
* Punto respecto del cual se hace la rotación, el utilisador lo fija al
* presionar el Mouse en la pantalla después de haber seleccionado Manipular
* Rotar en el Menu.
*/
  private Point ancla;
/**
* Es el angulo entre la linea que pasa por el ancla y el punto donde el
* utilisador presionó el Mouse después de haber fijado el ancla y el
* horizontal.
*/
  private double anguloReferencia;
/**
* Cruz que aparece al lugar donde se fija el ancla.
*/
  private Cruz cruz;
 /**
 * estado permite saber a que nivel del escalamiento estamos.
 * estado == INICI0_NUEVA_ROTACION : el utilisador seleccionó Rotar en el
 * Menu Manipular.
 * pero no presionó el Mouse sobre la pantalla todavía.
 * estado == ANCLA_FIJADA : el utilisador presionó el Mouse sobre la pantalla
 * y fijó el ancla, pero no presionó el Mouse otra vez para definir
 * anguloReferencia.
 * estado == TERMINO_ESCALAMIENTO : el utilisador definó anguloReferencia al
 * presionar el mouse una secunda vez y está "drageando" el Mouse para
 * definir el angulo de rotación.
 */
  private int estado;
  private static final int INICIO_NUEVA_ROTACION = 0;
  private static final int ANCLA_FIJADA = 1;
  private static final int TERMINO_ROTACION = 2;
  
/**
* Construye un RotationHandler que permite actuar sobre el
* Dibujo dado en parametro.
* @param dib Dibujo sobre lo cual RotationHandler va a actuar.
*/
  public RotationHandler(Dibujo dib)
    {
      dibujo = dib;
      cruz = new Cruz();
    }
/**
* Metodo que cambia los MouseListener y MouseMotionListener asociados al
* Dibujo cuando el utilisador executa un acción.
* Los nuevos MouseListener y MouseMotionListener asociados al dibujo son
* respectivamente, después del llamado a este metodo, MouseHandler y
* MouseMotionHandler, clases internas a RotationHandler.
* Después del llamado a este metodo estado == INICIO_NUEVA_ROTATION.
* @param e ActionEvent que produzcó el llamado a este metodo.
*/ 
  public void actionPerformed(ActionEvent e)
    {
      dibujo.changeMouseListener(new MouseHandler());
      dibujo.changeMouseMotionListener(new MouseMotionHandler());
      estado = INICIO_NUEVA_ROTACION;
    }
/**
* Clase interna que maneja los eventos del Mouse después que el utilisador
* haya seleccionado Manipular Rotar en el menu.  
*/
  class MouseHandler extends MouseAdapter
  {
 
/**
* Cuando el utilisador hace clic con el Mouse en la pantalla después de haber
* seleccionado Manipular Rotar en el Menu, se graba las coordenadas del punto
* como las del ancla de la rotación y se dibuja una cruz en este lugar.
* Despues del llamado a este metodo, estado == ANCLA_FIJADA.
* @param event Evento que produzcó el llamado a este metodo.
*/ 
 public void mouseClicked(MouseEvent event)
      {
        if (estado == INICIO_NUEVA_ROTACION)
       {
          ancla = new Point(event.getPoint());
          dibujo.remueva(cruz);
          cruz.setPoint(ancla);
          dibujo.incorpore(cruz);
          estado = ANCLA_FIJADA;
        }
      }
    
/*
* Cuando el utilisador presiona el Mouse después de haber fijado el ancla, se
* mide el angulo entre la linea que pasa por el ancla y el punto donde el
* utilisador presionó el Mouse y el horizontal, para definir anguloReferencia.
* Si ya se definó anguloReferencia y el angulo de rotación,
* (es decir estado == TERMINO_ROTACION) el estado se vuelve igual a 
* INICIO_NUEVA_ROTACION. 
* @param event Evento que produzcó el llamado a este metodo.
*/
    public void mousePressed (MouseEvent event)
      {
       if (estado == ANCLA_FIJADA)
       {
          Point p = new Point (event.getPoint());
          double x = p.X - ancla.X;
          double y = p.Y - ancla.Y;
          anguloReferencia = Math.atan2(y, x);
        }
        if (estado == TERMINO_ROTACION)
            estado = INICIO_NUEVA_ROTACION;
      }
/**
* Cuando el utilisador deja de presionar el Mouse después de haber
* definido el angulo de rotación, se remueva la cruz y estado se vuelve
* igual a TERMINO_ROTACION. 
* @param event Evento que produzcó el llamado a este metodo.
*/    
    public void mouseReleased(MouseEvent event)
      {
        if (estado == ANCLA_FIJADA)
        {
          dibujo.remueva(cruz);
          estado = TERMINO_ROTACION;
        }
      }
  }

/**
* Clase que maneja los eventos de Movimiento del Mouse después que el utilisador
* haya seleccionado Manipular Rotar en el menu.  
*/  
  class MouseMotionHandler extends MouseMotionAdapter
  {

/**
* Cuando el utilisador "drag" el Mouse, el angulo de rotación ("delatAngulo")
* se vuelve igual al angulo entre la linea que pasa por el ancla y el punto
* donde se ubica el Mouse y el horizontal, llamado "nuevoAngulo", menos
* "anguloReferencia". Se rota la Figura de "deltaAngulo".
* Tras "anguloReferencia" se vuelve igual a "nuevoAngulo".
* @param event Evento del Mouse que produzcó el llamado a este metodo.
*/  
    public void mouseDragged(MouseEvent event)
      {
        if (estado == ANCLA_FIJADA)
        {
          Point p = new Point (event.getPoint());
          double x = p.X - ancla.X;
          double y = p.Y - ancla.Y;
          double nuevoAngulo = Math.atan2(y, x);
          double deltaAngulo = nuevoAngulo - anguloReferencia;
          dibujo.rotar(ancla, deltaAngulo);
          anguloReferencia = nuevoAngulo;
        }
      }
  }
  
}

