import java.awt.Graphics;
import java.awt.Color;
import java.awt.Point;

public class Punto
{
   // Tipos de punto
   public static final int NORMAL     = 0;
   public static final int HORIZONTAL = 1;
   public static final int VERTICAL   = 2;
   public static final int FIJO       = 3;
   public static final int IMPULSOR   = 4;
   public static final int OSCILANTE  = 5;

   // Rango de visualizacion de la pantalla
   public static double panX0 = 0.0;
   public static double panY0 = 0.0;
   public static double paniLx = 1.0;
   public static double paniLy = 1.0;

   // Valores maximo y minimo de las posiciones
   public static double minX = +1e100;
   public static double maxX = -1e100;
   public static double minY = +1e100;
   public static double maxY = -1e100;

   // Fuerzas actuantes
   public double fx,fy;

   // Masa
   public double masa = 1.0;
   private double im = 1.0;

   // Posicion y velocidad
   public  double[] q  = new double[4];
   public  double[] qc = new double[4];
   private double[] qs = new double[4];
   // Posicion y velocidad inicial
   private double[] qi = new double[4];

   // Posicion en la pantalla
   public int panX;
   public int panY;
   public int panVx;
   public int panVy;

   // Tipo de punto
   private int tipo = NORMAL;

   // Parametros de punto oscilante
   private double t, iniX, iniY, ampl, frec, fase, semiper;
   private boolean oscHor, oscVer;

   public Punto(double x, double y)
   {
      q[0] = x; q[1] = y; q[2] = 0.0; q[3] = 0.0;
      fx = 0.0; fy = 0.0;
      iniX = q[0]; iniY = q[1];
      t = 0.0;
      chequeaLimites();
   }

   public Punto( double[] p )
   {
      q[0] = p[0]; q[1] = p[1]; q[2] = 0.0; q[3] = 0.0;
      fx = 0.0; fy = 0.0;
      iniX = q[0]; iniY = q[1];
      t = 0.0;
      chequeaLimites();
   }

   public void reiniciar( )
   {
      q[0] = qi[0]; q[1] = qi[1]; q[2] = qi[2]; q[3] = qi[3];
      iniX = q[0]; iniY = q[1];
      fx = 0.0; fy = 0.0;
      t = 0.0;
      chequeaLimites();
   }

   public void chequeaLimites( )
   {
      if( q[0] < minX ) minX = q[0];
      if( q[0] > maxX ) maxX = q[0];
      if( q[1] < minY ) minY = q[1];
      if( q[1] > maxY ) maxY = q[1];
   }

   public void setMasa( double m )
   {
      masa = m;
      im = 1.0/masa;
   }

   public void calculaPosicion( int lx, int ly )
   {
      panX = (int) Math.round(lx*(q[0]-panX0)*paniLx);
      panY = (int) Math.round(ly*(q[1]-panY0)*paniLy);
   }

   public void calculaPosVel( int lx, int ly )
   {
      panVx = (int) Math.round(lx*(q[0]+q[2]-panX0)*paniLx);
      panVy = (int) Math.round(ly*(q[1]+q[3]-panY0)*paniLy);
   }

   public void moverPos( Point p, int lx, int ly )
   {
      q[0] = panX0 + p.x/(lx*paniLx);
      q[1] = panY0 + p.y/(ly*paniLy);
      if( (tipo == IMPULSOR) || (tipo == OSCILANTE) ) {
         iniX = q[0];
         iniY = q[1];
      }
      chequeaLimites();
   }

   public void moverVel( Point p, int lx, int ly )
   {
      q[2] = panX0 + p.x/(lx*paniLx) - q[0];
      q[3] = panY0 + p.y/(ly*paniLy) - q[1];
   }

   public void dibujar( Graphics g, boolean selec )
   {
      if(selec)
        g.setColor(Color.red);
      else
        switch(tipo) {
          case NORMAL:     g.setColor(Color.blue); break;
          case HORIZONTAL:
          case VERTICAL:   g.setColor(Color.green); break;
          case FIJO:       g.setColor(Color.darkGray); break;
          case IMPULSOR:   g.setColor(Color.cyan); break;
          case OSCILANTE:  g.setColor(Color.pink); break;
        }
      g.fillOval(panX-3,panY-3,7,7);
      if( tipo == HORIZONTAL ) {
        g.setColor(Color.blue);
        g.drawLine(panX-3,panY,panX+3,panY);
      }
      if( tipo == VERTICAL ) {
        g.setColor(Color.blue);
        g.drawLine(panX,panY-3,panX,panY+3);
      }
   }

   public void dibujarVeloc( Graphics g )
   {
      g.setColor(Color.magenta);
      g.drawLine(panX,panY,panVx,panVy);
      g.drawOval(panVx-2,panVy-2,4,4);
   }

   public void rungeKuttaPreparacion( )
   {
      qi[0] = q[0]; qi[1] = q[1]; qi[2] = q[2]; qi[3] = q[3];
   }

   public void rungeKuttaInic( )
   {
      qs[0] = q[0]; qs[1] = q[1]; qs[2] = q[2]; qs[3] = q[3];
      qc[0] = q[0]; qc[1] = q[1]; qc[2] = q[2]; qc[3] = q[3];
      fx = 0.0; fy = 0.0;
   }

   public void rungeKuttaIter( double hs, double hc )
   {
      if( tipo == FIJO ) return;
      if( tipo == IMPULSOR ) {
         if(t+hc>semiper) return;
         if(oscHor) qc[0] = iniX + ampl*Math.sin(frec*(t+hc));
         if(oscVer) qc[1] = iniY - ((oscHor) ? ampl*Math.cos(frec*(t+hc))
                                             : ampl*Math.sin(frec*(t+hc)));
         return;
      }
      if( tipo == OSCILANTE ) {
         if(oscHor) qc[0] = iniX + ampl*Math.sin(frec*(t+hc));
         if(oscVer) qc[1] = iniY - ((oscHor) ? ampl*Math.cos(frec*(t+hc))
                                             : ampl*Math.sin(frec*(t+hc)));
         return;
      }
      if( tipo != VERTICAL ) {
        qs[0] += hs*qc[2];
        qs[2] += hs*fx*im;
        qc[0] = q[0] + hc*qc[2];
        qc[2] = q[2] + hc*fx*im;
      }
      if( tipo != HORIZONTAL ) {
        qs[1] += hs*qc[3];
        qs[3] += hs*fy*im;
        qc[1] = q[1] + hc*qc[3];
        qc[3] = q[3] + hc*fy*im;
      }
      fx = fy = 0.0;
   }

   public void rungeKuttaFinal( double h )
   {
      if( tipo == FIJO ) {
        chequeaLimites();
        return;
      }
      if( tipo == IMPULSOR ) {
         t += h;
         if(t>semiper) return;
         if(oscHor) q[0] = iniX + ampl*Math.sin(frec*t);
         if(oscVer) q[1] = iniY - ((oscHor) ? ampl*Math.cos(frec*t)
                                            : ampl*Math.sin(frec*t));
         chequeaLimites();
         return;
      }
      if( tipo == OSCILANTE ) {
         t += h;
         if(oscHor) q[0] = iniX + ampl*Math.sin(frec*t);
         if(oscVer) q[1] = iniY - ((oscHor) ? ampl*Math.cos(frec*t)
                                            : ampl*Math.sin(frec*t));
         chequeaLimites();
         return;
      }
      if( tipo != VERTICAL ) {
        q[0] = qs[0] + h*qc[2];
        q[2] = qs[2] + h*fx*im;
      }
      if( tipo != HORIZONTAL ) {
        q[1] = qs[1] + h*qc[3];
        q[3] = qs[3] + h*fy*im;
      }
      chequeaLimites();
   }

   public void azarPosicion( )
   {
      if( tipo == FIJO ) return;
      if( tipo == IMPULSOR ) return;
      if( tipo == OSCILANTE ) return;
      if(tipo != VERTICAL)   q[0] = panX0 + Math.random()/paniLx;
      if(tipo != HORIZONTAL) q[1] = panY0 + Math.random()/paniLx;
      chequeaLimites();
   }

   public void azarVelocidad( )
   {
      if( tipo == FIJO ) return;
      if( tipo == IMPULSOR ) return;
      if( tipo == OSCILANTE ) return;
      if(tipo != VERTICAL)   q[2] = 2*Math.random()-1.0;
      if(tipo != HORIZONTAL) q[3] = 2*Math.random()-1.0;
   }

   public void azarMasa( )
   {
      double e = 10.0*Math.random()-5.0;
      setMasa( Math.pow(10.0,e) );
   }

   public void setTipo( int tipo )
   {
      this.tipo = tipo;
   }

   public void setTipoOscilante( double a, double w, boolean hor,
                                 boolean ver, boolean impulsor )
   {
      tipo = (impulsor) ? IMPULSOR : OSCILANTE;
      oscHor = hor;
      oscVer = ver;
      ampl = a;
      frec = 2.0*Math.PI*w;
      semiper = 1.0/(2.0*w);
      iniX = q[0];
      iniY = q[1];
      t = 0.0;
   }

   public void setAmplOsc( double valor )
   {
      ampl = valor;
   }

   public void setFrecOsc( double valor )
   {
      frec = 2.0*Math.PI*valor;
      semiper = 1.0/(2.0*valor);
   }
}
