import java.awt.* ;
import java.awt.event.* ;
import java.applet.* ;

public class RollingCircle extends Applet
                                implements Runnable, ActionListener {
  Thread             th = null ;
  static final float rInner = 90 ;
  static final float rOuter = 30 ;
  int                cWidth, cHeight ;
  int                t = 0 ; 
  Image              backImg = null ;
  Graphics           backG = null ;
  Button             startStop = new Button("Start") ;
  Button             resetBtn  = new Button("Reset") ;
  Button             stepBtn   = new Button("Step") ;
  boolean            isRunning = false ;
  
  public void init() {
    add(startStop) ; add(resetBtn) ; add(stepBtn) ;
    startStop.addActionListener(this) ;
    resetBtn.addActionListener(this) ;
    stepBtn.addActionListener(this) ;
  }

  public void actionPerformed(ActionEvent e) {
    if( e.getSource() == startStop ) {
      isRunning = !isRunning ;
      if( isRunning ) {
        startStop.setLabel("Stop") ;
      }
      else {
        startStop.setLabel("Start") ;
      }
      startStop.repaint() ;
    }
    else if( e.getSource() == resetBtn ) {
      isRunning = false ;
      t = 0 ;
      startStop.setLabel("Start") ;
      startStop.repaint() ;
      repaint() ;
    }
    else if( e.getSource() == stepBtn ) {
      if( !isRunning ) {
        t = (t+5)%360 ;
        repaint() ;
      }
    }
  }
  
  private void drawInner() {
    backG.setColor(Color.white) ;
    backG.fillRect(0,0,cWidth,cHeight) ;
    backG.setColor(Color.red) ;
    backG.drawLine(0, cHeight/2, cWidth-1, cHeight/2) ;
    backG.drawLine(cWidth/2, 0, cWidth/2, cHeight-1) ;
    backG.setColor(Color.black) ;
    backG.drawOval( cWidth/2-(int)rInner, cHeight/2-(int)rInner,
                    (int)rInner*2, (int)rInner*2 ) ;
  }

  private void drawOuter() {
    int cx, cy ;
    int px, py ;
    int t2 ;
    cx = (int)((rInner+rOuter)*Math.cos((90.0-(float)t)*Math.PI/180.0)+0.5) ;
    cy = (int)((rInner+rOuter)*Math.sin((90.0-(float)t)*Math.PI/180.0)+0.5) ;
    px = cx - (int)(rOuter*Math.cos((90.0-(float)t*(1.0+rInner/rOuter))*Math.PI/180.0)+0.5) ;
    py = cy - (int)(rOuter*Math.sin((90.0-(float)t*(1.0+rInner/rOuter))*Math.PI/180.0)+0.5) ;
    backG.setColor(Color.black) ;
    for( t2=0 ; t2<360 ; t2+=120 ) {
      backG.fillArc( cWidth/2 +cx - (int)rOuter,
                     cHeight/2-cy - (int)rOuter,
                     (int)rOuter*2, (int)rOuter*2,
                     (int)(-(float)t*(1.0+rInner/rOuter))+t2-60, 60) ;
    }
    backG.setColor(Color.blue) ;
    backG.drawOval( cWidth/2 +cx - (int)rOuter,
                    cHeight/2-cy - (int)rOuter,
                    (int)rOuter*2, (int)rOuter*2 ) ;
    backG.drawLine( cWidth/2 +cx, cHeight/2 -cy,
                    cWidth/2 +px, cHeight/2 -py ) ;
    backG.fillOval( cWidth/2 +px-3, cHeight/2 -py-3, 6,6 ) ;
  }

  public void start() {
    cWidth  = getSize().width ;
    cHeight = getSize().height ;
    backImg = createImage(cWidth, cHeight) ;
    backG = backImg.getGraphics() ;
    repaint() ;
    th = new Thread(this) ;
    th.start() ;
  }

  public void run() {
    while( th != null ) {
      repaint() ;
      try {
        th.sleep(50) ;
      }
      catch(InterruptedException e) {
      }
      if( isRunning ) {
        t = (t+5)%360 ;
      }
    }
  }

  public void update(Graphics g) {
    paint(g) ;
  }
  public void paint(Graphics g) {
    if( backImg != null ) {
      drawInner() ;
      drawOuter() ;
      g.drawImage(backImg,0,0,this) ;
    }
  }

}