import java.io.*;
import java.util.*;
import java.awt.*;
import java.awt.event.*;

class Dot implements Runnable {
   Thread runner;
   float left;    // current pixels in from left       
   float top;     // current pixels in from top        
   float x;       // destination pixels in from left   
   float y;       // destination pixels in from top    
   int number;    // identification of dot             
   Color color;   // color of dot                      
   int dia = 30;  // diameter of dot                   
   float xdiff=0; // difference between mouse pos and  
   float ydiff=0; // top left corner of dot            

   public Dot(int n, Color clr, int left, int top) {
      number = n;
      color = clr;
      this.left = x = (float)left;
      this.top  = y = (float)top;
      runner = new Thread(this);
      runner.start();
   }

   public boolean over (int x, int y) { // returns true iff mouse is over dot
                                        // x and y are mouse coordinates     
      if (x > left && left+dia > x && y > top && top+dia > y)
         return true;
      else
         return false;
   }

   void setPosition(int l, int t) {
      top  = (float)t - ydiff;
      left = (float)l - xdiff;
   }

   void  setDiff(int x, int y) { xdiff = x - left; ydiff = y - top; }
   
   public void run () {
      while (true) {
         try {
            runner.sleep(40);
         }
         catch (InterruptedException e) {  break;  }
         left = ((float)(.99*(left - x) + x));
         top  = ((float)(.99*(top - y) + y));
      }
   }
   
   public void suspend () { runner.suspend(); }
   public void resume ()  { runner.resume(); }   
   
   public void paintDot(Graphics g, FontMetrics fm) {
      String lbl = String.valueOf(number);
      int w = fm.stringWidth(lbl);
      int h = fm.getHeight();
      g.setColor(color);
      g.fillOval((int)left, (int)top, dia, dia);
      g.setColor(Color.white);
      g.drawString(lbl, (int)left-w/2+dia/2, (int)top+12+h/2);
   }
}

class Cell {
   Dot obj;
   Cell next;
   Cell prev;
   
   Cell (Dot obj, Cell next, Cell prev) {
      this.obj = obj;
      this.next = next;
      this.prev = prev;
   }
}

class MyObjectList {
   Cell head = null;
   Cell tail = null;
   
   void add (Dot obj) {
      if (head == null)
         head = tail = new Cell(obj, null, null);
      else {
         head = new Cell(obj, head, null);
         head.next.prev = head;         
      }
   }
   
   Dot findAndMoveToTop (int x, int y) {
      Cell a,b,c;

      if (head == null) return null;
      for (c=head ; c != null ; c=c.next) {
         if (c.obj.over(x,y)) {            
            if (head != c) {
               a = c.prev;
               b = c.next;
               a.next = b;
               if (b != null) b.prev = a; else tail = a;               
               c.next = head;
               c.prev = null;
               head.prev = c;
               head = c;
            }
            return head.obj;            
         }
      }
      return null;
   }

   void display (Graphics g, FontMetrics f) {
      for (Cell cell=tail ; cell != null ; cell = cell.prev)
         cell.obj.paintDot(g,f);
   }
}

class RB1_Frame extends Frame implements Runnable, MouseMotionListener {

   Thread runner;
   Dot pick;
   Color color;
   MyObjectList mol;
   
   MouseAdapter ma = new MouseAdapter() {
      public void mousePressed (MouseEvent event) {
         pick = mol.findAndMoveToTop(event.getX(),event.getY());
         if (pick != null) {
            pick.setDiff(event.getX(),event.getY());
            pick.suspend();
         }
      }
      
      public void mouseReleased (MouseEvent event) {
         if (pick == null) return;
         pick.resume();
         pick = null;
      }
   };

   public RB1_Frame() {
      double r = 150;
      double pi = 355.0/113.0;
      double c = 1.0/16.0;
      int x,y;
      mol = new MyObjectList();
      
      for (int i=0 ; i < 8 ; i++, c+=0.125) {
         color = Color.getHSBColor((float)c, (float)1.0, (float)1.0);
         x = (int)(r*(Math.sin(c*2*pi)+1.8));
         y = (int)(r*(Math.cos(c*2*pi)+1.35));
         mol.add(new Dot(i+1,color,x,y));
      }

      runner = new Thread(this);
      runner.start();
      addMouseListener(ma);
      addMouseMotionListener(this);
      repaint();
   }

   public void run() {
      while (true) {
         try { runner.sleep(100);  }
         catch (Exception e) {  }
         repaint();
      }
   }
   
   Image offscreen;
   Dimension offscreensize;
   Graphics offgraphics;

   // double buffering   
   public void update(Graphics g) {
      Dimension d = getSize();
      if ((offscreen == null) || (d.width != offscreensize.width) ||
          (d.height != offscreensize.height)) {
         offscreen = createImage(d.width, d.height);
         offscreensize = d;
         offgraphics = offscreen.getGraphics();
         offgraphics.setFont(getFont());
      }

      offgraphics.setColor(new Color(192,192,192));
      offgraphics.fillRect(0, 0, d.width, d.height);
      
      FontMetrics fm = g.getFontMetrics();
      mol.display(offgraphics,fm);
      g.drawImage(offscreen, 0, 0, null);
   }

   public void mouseMoved (MouseEvent event) {  }
   
   public void mouseDragged (MouseEvent event) {
      if (pick != null) pick.setPosition(event.getX(),event.getY());
   }
}

public class RB1_App {
   public static void main (String arg[]) {
      RB1_Frame rb1 = new RB1_Frame();
      rb1.setSize(570,450);
      rb1.show();
   }
}