/*
Ray's First Applet.

This is meant to become a demonstration of a 
"deterministic finite state automaton".   It
animates the parsing of an input string, 
character by character, by following arcs
from node to node as each character is read.
*/

/*
To Do:

Make the arcs elliptic instead of circular. This should cause the lines to 
rise and descend more quickly near the nodes, and be broader and flatter,
leaving more room for the.

Make Nodes and Arcs into bona fide classes.  These would then be instantiated 
in the init method, and then the paint method would simply cause them to each
render themselves.  Nodes would acquire attributes "boolean start", "boolean stop"
and "boolean current".  dfsa would acquire attributes like current node and current
arc.  
*/

import java.applet.Applet ;
import java.awt.Button ;
import java.awt.Event ;
// import java.awt.event.ActionListener ;
// import java.awt.event.ActionEvent ;
import java.awt.Color ;
import java.awt.FontMetrics ;
import java.awt.Graphics ;
import java.awt.Label ;
import java.lang.String ;
import java.awt.TextField ;
import java.lang.Object ;
import java.lang.Math ;

/** @deprecated jdk 1.0 */
public class dfsa extends Applet // implements ActionListener
{
Node aNode ;
Node bNode ;
Node cNode ;
Node dNode ;
Arc aArc ;
Arc bArc ;
Arc cArc ;
Arc dArc ;
Arc eArc ;

Label my_label ;
TextField my_textfield ;
Button start_button ;
Button step_button ;
Button clear_button ;
int position = 0 ;
static final String START = "Start" ;
static final String STEP = "Step" ;
static final String CLEAR = "Clear" ;

public void init()
{
setBackground( Color.white ) ;
my_label = new Label( "Input" ) ;
my_textfield = new TextField( 20 ) ;
start_button = new Button( "Start" ) ;
step_button = new Button( "Step" ) ;
clear_button = new Button( "Clear" ) ;
// start_button.setActionCommand( START ) ;
// step_button.setActionCommand( STEP ) ;
// clear_button.setActionCommand( CLEAR ) ;
// start_button.addActionListener( this ) ;
// step_button.addActionListener( this ) ;
// clear_button.addActionListener( this ) ;

this.add( my_label ) ;
this.add( my_textfield ) ;
this.add( start_button ) ;
this.add( step_button ) ;
this.add( clear_button ) ;

aNode = new Node(  30 , 128 , "a" ) ;
bNode = new Node( 130 , 128 , "b" ) ;
cNode = new Node( 230 , 128 , "c" ) ;
dNode = new Node( 330 , 128 , "error" ) ;
aNode.setStart() ;
cNode.setStop() ;
dNode.setStop() ;

aNode.setCurrent() ;

aArc = new Arc( aNode , cNode , "1" ) ;
bArc = new Arc( aNode , bNode , "0,2,4" ) ;
cArc = new Arc( bNode , cNode , "1" ) ;
dArc = new Arc( aNode , dNode , "*" ) ;
eArc = new Arc( bNode , dNode , "*" ) ;

dArc.setCurrent() ;
}

// public void actionPerformed( ActionEvent e )
public boolean action( Event e , Object o )
{
if ( e.id  == Event.ACTION_EVENT ) 
{
String command = o.toString() ;
if ( command == START )
  {
  position = 0 ;
  // my_textfield.setCaretPosition( position ) ;
  my_textfield.select( position , position + 1 ) ;
  }
else if ( command == STEP )
  {
  position ++ ;
  // my_textfield.setCaretPosition( position ) ;
  my_textfield.select( position , position + 1 ) ;
  }
else if ( command == CLEAR )
  {
  position = 0 ;
  my_textfield.setText( "" ) ;
  }
}
return true ;
}

public void paint( Graphics g )
{
aNode.draw( g ) ;
bNode.draw( g ) ;
cNode.draw( g ) ;
dNode.draw( g ) ;
aArc.draw( g ) ;
bArc.draw( g ) ;
cArc.draw( g ) ;
dArc.draw( g ) ;
eArc.draw( g ) ;
}

}

class Arc {
private Node fromNode = null ;
private Node toNode = null ;
private boolean currentArc = false ;
private String name = "" ;

public Arc( Node fromNode , Node toNode )
{
this.fromNode = fromNode ;
this.toNode = toNode ;
}

public Arc( Node fromNode , Node toNode , String name )
{
this.fromNode = fromNode ;
this.toNode = toNode ;
this.name = name ;
}

public void setCurrent()
{
this.currentArc = true ;
}

public void setNotCurrent()
{
this.currentArc = false ;
}

public void draw( Graphics g )
{
int x0 = 0 ;
int y0 = 0 ;
int x1 = 0 ;
int y1 = 0 ;
double deltax ;
double deltay ;
double R ;
double theta1 ;
double theta2 ;
double x ;
double y ;
double theta ;
double lambda ;
FontMetrics fontMetrics ;

x0 = this.fromNode.x ;
y0 = this.fromNode.y ;
x1 = this.toNode.x ;
y1 = this.toNode.y ;

deltax = Math.abs( ( x0 - x1 ) / 2.0 ) ;
deltay = 0.5 * Math.sqrt( 2.0 ) * deltax ;
R = Math.sqrt( deltax * deltax + deltay * deltay ) ;
theta1 = Math.acos( deltax / R ) ;
theta2 = Math.asin( 4.0 / R ) ;
x = x0 + deltax - R ;
y = y0 + deltay - R ;
theta = theta1 + 2.0 * theta2 ;
lambda = Math.PI - 2.0 * theta ;

if ( this.currentArc )
  g.setColor( Color.green ) ;

g.drawArc( (int) ( x + 0.5 ) , (int) ( y + 0.5 ) , (int) ( 2.0 * R + 0.5 ) , (int) ( 2.0 * R + 0.5 ) , 
(int) ( theta * 180.0 / Math.PI + 0.5 ) , (int) ( lambda * 180.0 / Math.PI + 0.5 ) ) ;

if ( this.currentArc )
  g.setColor( Color.black ) ;
  
if ( this.name != "" )
  {
  fontMetrics = g.getFontMetrics() ;
  x = x0 + deltax - fontMetrics.stringWidth( this.name ) / 2.0 ;
  y = y0 + deltay - R - 2.0 ;
  g.drawString( this.name , (int) ( x ) , (int) ( y ) ) ;
  }
}
}

class Node
{
public final int RADIUS = 8 ;
public int x = 0 ;
public int y = 0 ;
private boolean currentNode = false ;
private boolean startNode = false ;
private boolean stopNode = false ;
private String name = "" ;

public Node( int x , int y )
{
this.x = x ;
this.y = y ;
}

public Node( int x , int y , String name )
{
this.x = x ;
this.y = y ;
this.name = name ;
}

public void setStart( )
{
this.startNode = true ;
}

public void setStop( )
{
this.stopNode = true ;
}

public void setCurrent( )
{
this.currentNode = true ;
}

public void setNotCurrent()
{
this.currentNode = false ;
}

public void getXY( int x , int y )
{
x = this.x ;
y = this.y ;
}

public void draw( Graphics g )
{
FontMetrics fm ;

if ( this.currentNode )
  {
  g.setColor( Color.green ) ;
  g.fillOval( x - RADIUS , y - RADIUS , RADIUS * 2 , RADIUS * 2 ) ;
  g.setColor( Color.black ) ;
  }
else
  {
  g.setColor( Color.white ) ;
  g.fillOval( x - RADIUS , y - RADIUS , RADIUS * 2 , RADIUS * 2 ) ;
  g.setColor( Color.black ) ;
  }
g.drawOval( this.x - RADIUS , this.y - RADIUS , RADIUS * 2 , RADIUS * 2 ) ;
if ( this.stopNode )
  {
  g.setColor( Color.red ) ;
  g.drawOval( this.x - RADIUS * 3 / 4 , this.y - RADIUS * 3 / 4 , RADIUS * 3 / 2 , RADIUS * 3 / 2 ) ;
  g.setColor( Color.black ) ;
  }
if ( this.startNode ) 
  {
  g.drawLine( this.x - RADIUS * 2 , this.y - RADIUS / 2 , this.x - RADIUS , this.y ) ;
  g.drawLine( this.x - RADIUS * 2 , this.y + RADIUS / 2 , this.x - RADIUS , this.y ) ;
  }
if ( this.name != "" )
  {
  fm = g.getFontMetrics() ;
  g.drawString( this.name , this.x + RADIUS * 3 / 2 , this.y + fm.getHeight() / 2 ) ;
  }
}

}
