import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Graphics;
import java.awt.Color;

public class Malla
{
   public static final int CUADRADA = 0;
   public static final int HEXAGONAL = 1;
   public static final int CIRCULAR = 2;

   private int tipo = -1;
   private boolean[] con;
   private Rectangle[] rect;
   private int imgLx = -1;
   private int imgLy = -1;

   public Malla( int tipo )
   {
     setTipo(tipo);
   }

   public int getTipo( ) { return(tipo); }

   public void setTipo( int tipo )
   {
      if( tipo == this.tipo ) return;
      this.tipo = tipo;
      switch(this.tipo) {
        case CUADRADA:
          con = new boolean[4];
          con[0] = false; con[1] = true; con[2] = false; con[3] = true;
          rect = new Rectangle[8];
          break;
        case HEXAGONAL:
          con = new boolean[3];
          con[0] = true; con[1] = true; con[2] = true;
          rect = new Rectangle[6];
          break;
     // case CIRCULAR:
        default:
          con = new boolean[2];
          con[0] = true; con[1] = true;
          rect = new Rectangle[4];
          break;
      }
      for(int i=0; i<rect.length; i++) rect[i] = new Rectangle();
      calculaRectangulos();
   }

   public int numPuntos( int ly, int lx )
   {
      switch(tipo) {
        case CUADRADA:
          return(lx*ly);
        case HEXAGONAL:
          int lyi = ly/2;
          int lyp = ly-lyi;
          return(lyp*(lx-1) + lyi*lx);
     // case CIRCULAR:
        default:
          return(lx*ly);
      }
   }

   public double[] getPosPunto( int n, int ly, int lx )
   {
      double fac = Math.sqrt(3.0)/2.0;
      double[] vec = new double[2];

      switch(tipo) {
        case CUADRADA:
          vec[0] = (double) (n%lx);
          vec[1] = (double) (n/lx);
          break;
        case HEXAGONAL:
          int l2 = 2*lx-1;
          int y = 2*(n/l2);
          int x = n%l2;
          if( x >= lx-1 ) { x -= lx-1; y++; }
          vec[0] = x + ( (y%2==0) ? 0.5 : 0.0 );
          vec[1] = fac*y;
          break;
     // case CIRCULAR:
        default:
          double r = (n/lx) + 1.0;
          double ang = (2.0*Math.PI*(n%lx))/lx;
          vec[0] = r*Math.cos(ang);
          vec[1] = r*Math.sin(ang);
          break;
      }
      return(vec);
   }

   public int numConexiones( int ly, int lx )
   {
      int n = 0;
      switch(tipo) {
        case CUADRADA:
          if(con[0]) n += (ly-1)*(lx-1);
          if(con[1]) n += (ly-1)*lx;
          if(con[2]) n += (ly-1)*(lx-1);
          if(con[3]) n += ly*(lx-1);
          break;
        case HEXAGONAL:
          int lyi = ly/2;
          int lyp = ly-lyi;
          if(con[0]) n += (ly-1)*(lx-1);
          if(con[1]) n += (ly-1)*(lx-1);
          if(con[2]) n += lyp*(lx-2) + lyi*(lx-1);
          break;
     // case CIRCULAR:
        default:
          if(con[0]) n += ly*lx;
          if(con[1]) n += (ly-1)*lx;
          break;
      }
      return(n);
   }

   public int[] getVecPunto( int np, int ly, int lx )
   {
      int nvec = 0;
      int y,x;
      // Contar el numero de vecinos conectados
      switch(tipo) {
        case CUADRADA:
          y = np/lx;
          x = np%lx;
          if(con[0] && (y>0) && (x>0))    nvec++;
          if(con[1] && (y>0))             nvec++;
          if(con[2] && (y>0) && (x<lx-1)) nvec++;
          if(con[3] && (x>0))             nvec++;
          break;
        case HEXAGONAL:
          int l2 = 2*lx-1;
          y = 2*(np/l2);
          x = np%l2;
          if( x >= lx-1 ) { x -= lx-1; y++; }
          if( y%2 == 0 ) {
            if(con[0] && (y>0)) nvec++;
            if(con[1] && (y>0)) nvec++;
            if(con[2] && (x>0)) nvec++;
          } else {
            if(con[0] && (x>0))    nvec++;
            if(con[1] && (x<lx-1)) nvec++;
            if(con[2] && (x>0))    nvec++;
          }
          break;
     // case CIRCULAR:
        default:
          y = np/lx;
          x = np%lx;
          if(con[0]) nvec++;
          if(con[1] && (y<ly-1)) nvec++;
          break;
      }
      int[] vec = new int[nvec];
      int i = 0;
      switch(tipo) {
        case CUADRADA:
          if(con[0] && (x>0) && (y>0))    { vec[i] = np-lx-1; i++; }
          if(con[1] && (y>0))             { vec[i] = np-lx;   i++; }
          if(con[2] && (x<lx-1) && (y>0)) { vec[i] = np-lx+1; i++; }
          if(con[3] && (x>0))             { vec[i] = np-1;    i++; }
          break;
        case HEXAGONAL:
          if( y%2 == 0 ) {
            if(con[0] && (y>0)) { vec[i] = np-lx;   i++; }
            if(con[1] && (y>0)) { vec[i] = np-lx+1; i++; }
            if(con[2] && (x>0)) { vec[i] = np-1;    i++; }
          } else {
            if(con[0] && (x>0))    { vec[i] = np-lx;   i++; }
            if(con[1] && (x<lx-1)) { vec[i] = np-lx+1; i++; }
            if(con[2] && (x>0))    { vec[i] = np-1;    i++; }
          }
          break;
     // case CIRCULAR:
        default:
          if(con[0] && (x<lx-1))  { vec[i] = np+1;    i++; }
          if(con[0] && (x==lx-1)) { vec[i] = np-lx+1; i++; }
          if(con[1] && (y<ly-1))  { vec[i] = np+lx;   i++; }
          break;
      }
      return(vec);
   }

   private void calculaRectangulos( )
   {
      switch(tipo) {
        case CUADRADA:
          rect[0].x = rect[3].x = rect[5].x = imgLx/4;
          rect[1].x = rect[6].x             = imgLx/2;
          rect[2].x = rect[4].x = rect[7].x = (3*imgLx)/4;

          rect[0].y = rect[1].y = rect[2].y = imgLy/4;
          rect[3].y = rect[4].y             = imgLy/2;
          rect[5].y = rect[6].y = rect[7].y = (3*imgLy)/4;
          break;
        case HEXAGONAL:
          rect[0].x = rect[4].x = (3*imgLx)/8;
          rect[2].x             = imgLx/4;
          rect[3].x             = (3*imgLx)/4;
          rect[1].x = rect[5].x = (5*imgLx)/8;

          rect[0].y = rect[1].y = imgLy/4;
          rect[2].y = rect[3].y = imgLy/2;
          rect[4].y = rect[5].y = (3*imgLy)/4;
          break;
     // case CIRCULAR:
        default:
          rect[0].x             = imgLx/4;
          rect[1].x = rect[2].x = imgLx/2;
          rect[3].x             = (3*imgLx)/4;

          rect[0].y = rect[3].y = imgLy/2;
          rect[1].y             = imgLy/4;
          rect[2].y             = (3*imgLy)/4;
          break;
      }
      for( int i = 0; i < rect.length; i++ ) {
        rect[i].x -= 10;
        rect[i].y -= 10;
        rect[i].width = 20;
        rect[i].height = 20;
      }
   }

   private int traducePos( int i )
   {
      switch(tipo) {
        case CUADRADA:  return( (i<4) ? i : 7-i );
        case HEXAGONAL: return( (i<3) ? i : 5-i );
     // case CIRCULAR:
        default:        return( (i<2) ? i : 3-i );
      }
   }

   public void dibujar( Graphics g, int lx, int ly )
   {
      if( (lx != imgLx) || (ly != imgLy) ) {
        imgLx = lx;
        imgLy = ly;
        calculaRectangulos();
      }
      g.setColor( Color.cyan.brighter() );
      g.fillRect(0,0,imgLx,imgLy);
      int x0 = imgLx/2;
      int y0 = imgLy/2;
      for( int i = 0; i < rect.length; i++ ) {
        int n = traducePos(i);
        g.setColor( (con[n]) ? Color.red : Color.blue );
        g.fillOval(rect[i].x,rect[i].y,rect[i].width,rect[i].height);
        if( con[n] ) g.drawLine(rect[i].x+10,rect[i].y+10,x0,y0);
      }
      g.setColor(Color.black);
      g.fillOval(x0-10,y0-10,20,20);
   }

   public void pulsacion( Point pto, int lx, int ly )
   {
      if( (lx != imgLx) || (ly != imgLy) ) {
        imgLx = lx;
        imgLy = ly;
        calculaRectangulos();
      }
      int i = rect.length-1;
      while( (i>=0) && !rect[i].contains(pto) ) i--;
      if(i < 0) return;
      int n = traducePos(i);
      con[n] = !con[n];
   }
}
