import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.applet.*;


// This is a applet that animates a rotating spiral
public class SpiralAnimation5 extends Applet implements Runnable {

   // the size of this applet
   private final static int sizeX = 128;
   private final static int sizeY = 128;

   // the center of the applet
   private int cx=sizeX/2; 
   private int cy=sizeY/2;

   // the variable center of the spiral. 
   // It moves when the user focus on the spiral
   private int varX = cx;
   private int varY = cy;

   //the number of points on the spiral
   private int counter = 32;    

   // the cordinates of the previous point while calculating
   // the points of the spiral. Used to draw a line between the points
   private int last_x=0; 
   private int last_y=0;

   //the current color
   private Color color = Color.lightGray;

   // The starting step of the spiral
   private double step = 6.00;

   // The increasing step of the spiral
   private double stepInc = 0.0005;

   // Two booleans used to allow directional change of counters
   private boolean up = true;
   private boolean more = true;

   // The increasing step for color cycling
   private float colorIncStep = 0.000001f;
   private int spiralLength=32;

   // The drawing thread
   private Thread animThread; 

   // The graphics buffer
   private Graphics offgc;
   // An image of the graphics buffer
   Image offscreen = null;

   // The dimension of the applet
   Dimension d;

   // Run gets invoked by the system when we call myThread.start()
   // It executes repaint forever
   public void run() { 
      while(true) {
           pause(73);  
           repaint(); 
      }
   }

   // Pauses the running thread for mil milliseconds
   public void pause(int mil) { 
      try { 
        Thread.currentThread().sleep(mil);
      } catch (Exception ex) { 	
         ex.printStackTrace();
       }	
   } 

   // Initialises the Applet
   public void init() { 
     animThread = new Thread(this);
   }

   // Starts the Applet
   public void start() { 
     d = size();

     //Event handling mechanism for changing properties
     //of the spiral 
     addMouseListener(new MouseAdapter() { 
        public void mouseEntered(MouseEvent me) { 
              spiralLength*=2;
        }
        public void mouseExited(MouseEvent me) { 
             varX = cx;
	     varY = cy;
             spiralLength/=2;
        }
        public void mouseClicked(MouseEvent me) { 
             varX = cx;
	     varY = cy;
	}
     });
     addMouseMotionListener(new MouseMotionAdapter() { 
        public void mouseMoved(MouseEvent me) { 
          varX = me.getX(); 
          varY = me.getY();
        }
        public void mouseDragged(MouseEvent me) { 
          if (me.getY()>varY) {

          //dragged down
            if (colorIncStep+0.01f<0.99f) { 
               colorIncStep=colorIncStep+0.01f;   
            }             
          }
          else if (me.getY()<varY) {
            if (colorIncStep-0.01f>0) { 
              colorIncStep=colorIncStep-0.01f;  
            } 
 
           //dragged up
          }
          if (me.getX()>varX) { 
           counter+=10;
          }
	  else if (me.getX()<varX) {
           counter-=10;
          }
        }
     });

     // Preparetion for double buffering
     // creates the offscreen buffer and associated Graphics
     offscreen = createImage(d.width, d.height);
     offgc = offscreen.getGraphics();

     // Starts the thrad      
     animThread.start();
   }

   // the main method runs when we run the Spiral applet as an application
   // To do that we need to create a frame, instantiate the applet, put it
   // on the frame and call all the methods a browser would have called
   public static void main(String[] argv) { 

     Frame fr = new Frame("Spiral Animation") ; 
     SpiralAnimation5 sa = new SpiralAnimation5();
     fr.setLayout(new BorderLayout());
     fr.add(sa);
     fr.addWindowListener(new WindowAdapter() {
	public void windowClosing(WindowEvent we) {  
	   System.exit(0);	
	}
     });
     sa.init();
     fr.pack();
     fr.setVisible(true);
     sa.start();
   }   

   public Dimension getPreferredSize() { 
     return new Dimension(sizeX,sizeY);
   }
  
   public void update(Graphics g) { 
      paint(g);
   }

   public void paint(Graphics g) { 
      // clear the exposed area
      if (offgc!=null) { 
        offgc.setColor(getBackground());
        offgc.fillRect(0, 0, d.width, d.height);
        offgc.setColor(color);
        // do normal redraw
        draw(offgc);
        // transfer offscreen to window
        g.drawImage(offscreen, 0, 0, this);
      }
   }
  
  // Drawing spirals is based on the same principals as drawing circles
  // but with varying radius.
  public void draw(Graphics g) { 
     
     //long time = System.currentTimeMillis();
     int inCount = 1; 
     int x=0; int y=0; 
     last_x=0; last_y=0;
     counter--;
     if (counter<32) counter=64;
     for (int k=0; k<counter; k++) { 
            
      x = (int)(varX+inCount*Math.cos(k*cx/step));
      y = (int)(varY+inCount*Math.sin(k*cy/step));
      inCount++;   
      color = getNextColor();

      if (inCount>spiralLength)  { 
           inCount=0; 
           if (up) { 
               step+=stepInc; 
           } else step-=stepInc; 
           if (step>12) 
                up=false;
           else 
           if (step<3) 
                up=true;
      } 

      g.setColor(Color.gray);
      g.fillOval(cx-2,cy-2,4,4);
      g.fillRect(cx,cy,d.width-cx,1);

      g.setColor(Color.lightGray);
      g.fillRect(cx,cy+1,d.width-cx,1);
      g.fillOval(cx-1,cy-1,3,3);

      g.setColor(color);
      if (((x!=cx) && (y!=cy)) && ((last_x!=0) && (last_y!=0)) )  {
         g.drawLine(x, y,last_x, last_y);
         g.setColor(Color.gray);
         g.drawRect(x-1, y-1, 1 ,1 );
      } 
      last_x=x; last_y=y; 
      //System.out.print((System.currentTimeMillis()-time) + ",");

     }
    
   }

  float r=0.89f; float g=0.89f; float b=0.99f;

  public Color getNextColor() { 
    r-=colorIncStep;
    g-=(colorIncStep+colorIncStep);
    b-=(colorIncStep+colorIncStep+colorIncStep);
    if (r<=0f) { r=0.99f; } 
    if (g<=0f) { g=0.99f; } 
    if (b<=0f) { b=0.99f; }  

    //System.out.println("r:" + r + " g:" + g + "b:" + b);
    return(new Color(r,g,b));
  }
}
