/*
 * this applet draws mathmatical roses
 */


import java.applet.*;
import java.awt.*;
import java.io.*;
import java.lang.Math;
import java.util.*;

public class RoseApplet extends Applet {
	RosePanel dp;
	RoseControls cp;
	static int RWIDTH = 300, RHEIGHT = 450;

	public RoseApplet() {
	}

	public void init () {

		setLayout( new BorderLayout(10,10) );

		Rose rose = new Rose(31,13);
		setBackground( Color.black );
		dp = new RosePanel( rose );
		cp = new RoseControls(dp, rose);
		dp.setAssociatedControlPanel( cp );
		add( "South", cp );
		add( "Center", dp );
		reshape( 0, 0, RWIDTH, RHEIGHT );
	}

	public void start() {
		if ( dp.isLooping() && !(dp.isAlive()) ) {
			dp.start();
		}
	}

	public void stop() {
		if ( dp.isAlive() ) {
			dp.stop();
		}
	}

	public static void main( String args[] ) {
		Frame f = new RoseFrame( "Rose" );
		
		RoseApplet rose_a = new RoseApplet();
		rose_a.init();
		rose_a.start();

		f.add( "Center", rose_a );
		f.resize( RWIDTH, RHEIGHT );
		f.show();
	}
}



class RosePanel extends Panel implements Runnable {

	Rose rose;
	RoseControls control;
	boolean time_to_die;
	boolean looping;

	public RosePanel( RoseControls c, Rose rose ) {
		setBackground( Color.black );
		this.rose = rose;
		this.control = c;
	}

	public RosePanel( Rose rose ) {
		setBackground( Color.black );
		this.rose = rose;
	}

	public boolean isAlive() {
		return( !(this.time_to_die) );
	}

	public void setLooping( boolean l ) {
		looping = l;
	}

	public boolean isLooping() {
		return( looping );
	}

	public void setAssociatedControlPanel( RoseControls c ) {
		this.control = c;
	}

	public void run() {
		while ( !time_to_die ) {
			rose.setRandomRose();
			this.control.setLoop( rose.getNumLoops() );
			this.control.setDelta( rose.getDelta() );
			this.repaint();
			try {
				Thread.sleep(3000);
			}
			catch( InterruptedException e ) {
				return;
			}
		}
	}

	public void stop() {
		time_to_die = true;
	}

	public void start() {
		time_to_die = false;
		(new Thread(this)).start();
	}

	public void paint( Graphics g ) {
		String s = new String();
		int str_w, str_h, x, y;
		Rose rose = new Rose();
	
		Dimension d = size();
		setForeground(this.rose.getColor());
		setBackground(Color.black);
		this.rose.drawRose( g, d.width, d.height );
	}
}



// Class RoseFrame
//
// RoseFrame extends Frame to incorporate the proper handling of
// the window destroy event.

class RoseFrame extends Frame {
	public RoseFrame( String s ) {
		super( s );
		setTitle("Rose");
	}

	// window events are handled by the frame class
	public boolean handleEvent( Event e ) {
		switch( e.id ) {
		case Event.WINDOW_DESTROY:
			System.exit( 0 );
			return true;
		default:
			return super.handleEvent(e);
		}
	}
}



//
// class Rose
//
// the rose class handles the creation of the rose.  this class maintains
// inner variables describing the rose.  the rose is described
// by the number of loops and the delta.  a color value is stored for
// convenience.

class Rose extends Object {
	int lp;
	int dt;
	Color color;
	boolean random_started = false;

	public Rose() {
	}

	public Rose( int loops, int delta ) {
		lp = loops;
		dt = delta;
	}

	public void setNumLoops( int l ) {
		this.lp = l;
	}

	public int getNumLoops() {
		return( lp );
	}

	public void setDelta( int d ) {
		this.dt = d;
	}
	public int getDelta() {
		return( dt );
	}

	public void setColor( Color color ) {
		this.color = color;
	}

	public Color getColor () {
		return( color );
	}

	// rose algorithm is based on r=sin(lp*theta), where lp is the
	// number of loops and dt which is the amount theta is incremented
	// w/ each loop
	
	public void drawRose( Graphics g, int xmax, int ymax ) {
		int t;
		int newx, newy, offx, offy, origx, origy;
		int oldx, oldy;
		double r, rx, ry;
		double x;

		double twopi = 2 * Math.PI;

		t=0;
		oldx = offx = xmax/2;
		oldy = offy = ymax/2;
		origx = origy = 0;

		do {
			t = ((t + this.dt) % 360);
			x = (this.lp * t );
			double at =  t * twopi / 360;
			double ax =  x * twopi / 360;
			r = Math.sin(ax);

			rx = r*(xmax-5);
			ry = r*(ymax-5);

			newx = (int)(offx + (rx * Math.cos(at) ) / 2);
			newy = (int)(offy + (ry * Math.sin(at) ) / 2);

			// draw
			g.drawLine(oldx, oldy, newx, newy );

			oldx = newx;
			oldy = newy;
			Thread.currentThread().yield();
		} while ( t != 0 );
	}


	// this method randomly generates numloops and delta 
	Random r_lp, r_dt;
	public void setRandomRose() {
		if ( random_started == false ) {
			r_lp = new Random();
			r_dt = new Random();
		}
		int l = Math.abs(r_lp.nextInt() % 10000);
		int d = Math.abs(r_dt.nextInt() % 9999) ;
		setNumLoops( l );
		setDelta( d );
	}

}


class RoseControls extends Panel {
	RosePanel target;
	RoseApplet appl;
	Rose rose;
	TextField loop_txt;
	TextField delta_txt;
	Button random_btn, draw_btn;

	public RoseControls( RosePanel target, Rose rose ) {
		this.target = target;
		this.rose = rose;

//		setLayout(new GridLayout(2,3,10,7));
//		setLayout(new FlowLayout());
		GridBagLayout gridbag = new GridBagLayout();
		GridBagConstraints c = new GridBagConstraints();
		c.fill = GridBagConstraints.NONE;
		setFont( new Font("Helvetica", Font.PLAIN, 12) );
		setLayout( gridbag );

		setBackground(Color.lightGray);
		this.target.setForeground(Color.red);
		rose.setColor( Color.red );


		Choice shapes = new Choice();
		shapes.addItem("Red");
		shapes.addItem("Yellow");
		shapes.addItem("Orange");
		shapes.addItem("Cyan");
		shapes.addItem("Blue");
		shapes.addItem("Green");
		shapes.addItem("Pink");
		shapes.addItem("Magenta");
		c.weightx = 0.0;
		c.anchor = GridBagConstraints.CENTER;
		gridbag.setConstraints(shapes, c);
		add(shapes);

		Label loop_lbl = new Label(" Loops:", Label.RIGHT );
		c.gridwidth = GridBagConstraints.RELATIVE;
		gridbag.setConstraints(loop_lbl, c);
		add(loop_lbl);
		loop_txt = new TextField("31", 5);
		c.weightx = 0.0;
		c.gridwidth = GridBagConstraints.REMAINDER;
		gridbag.setConstraints(loop_txt, c);
		add(loop_txt);

		Label delta_lbl = new Label(" Delta:", Label.RIGHT );
		c.weightx=0.0;
		c.anchor = GridBagConstraints.EAST;
		c.gridwidth = GridBagConstraints.RELATIVE;
		add(delta_lbl);
		gridbag.setConstraints(delta_lbl, c);
		delta_txt = new TextField("13", 5);
		c.gridwidth = GridBagConstraints.REMAINDER;
		gridbag.setConstraints(delta_txt, c);
		add(delta_txt);


		draw_btn = new Button("Draw");
		c.ipadx = 3;
		c.weighty = .25;
		Insets ins = new Insets( 15, 2, 2, 5);
		c.insets = ins;
		c.fill = GridBagConstraints.HORIZONTAL;
		c.anchor = GridBagConstraints.CENTER;
		c.gridwidth = 1;
		gridbag.setConstraints(draw_btn, c);
		add(draw_btn);

		random_btn = new Button("Random");
		c.gridwidth = GridBagConstraints.RELATIVE;
		gridbag.setConstraints(random_btn, c);
		add(random_btn);

		Button stop_btn = new Button("Auto");
		c.gridwidth = GridBagConstraints.REMAINDER;
		gridbag.setConstraints(stop_btn, c);
		add(stop_btn);
	}

	public Insets insets() {
		return new Insets( 15, 5, 10, 10 );
	}

	public  void setLoop( int lp ) {
		loop_txt.setText( Integer.toString(lp) );
	}

	public  void setDelta( int dt ) {
		delta_txt.setText( Integer.toString(dt) );
	}

	public boolean action( Event ev, Object arg ) {
		int lp, dt;

		if ( ev.target instanceof Button ) {
			String label = (String)arg;

			if ( label.equals("Draw") ) {
				lp = Integer.parseInt(loop_txt.getText());
				dt = Integer.parseInt(delta_txt.getText());
				rose.setNumLoops(lp);
				rose.setDelta(dt);
				target.repaint();
			}
			else if ( label.equals("Random" ) ) {
				rose.setRandomRose();
				loop_txt.setText( Integer.toString(rose.getNumLoops()) );
				delta_txt.setText( Integer.toString(rose.getDelta()) );
				target.repaint();
			}
			else if ( label.equals("Stop") ) {
				((Button)ev.target).setLabel("Auto");
				target.stop();
				target.setLooping( false );
				random_btn.enable();
				draw_btn.enable();
			}
			else if ( label.equals ("Auto" ) ) {
				((Button)ev.target).setLabel("Stop");
				this.repaint();
				target.setLooping( true );
				target.start();
				random_btn.disable();
				draw_btn.disable();
			}
		}
		if ( ev.target instanceof Choice ) {
			String choice = (String)arg;
			
			if ( choice.equals("Red") ) {
				target.setForeground(Color.red);
				rose.setColor( Color.red );
			}
			else if ( choice.equals("Blue") ) {
				target.setForeground(Color.blue);
				rose.setColor( Color.blue );
			}
			else if ( choice.equals("Yellow") ) {
				target.setForeground(Color.yellow);
				rose.setColor( Color.yellow );
			}
			else if ( choice.equals("Green") ) {
				target.setForeground(Color.green);
				rose.setColor( Color.green );
			}
			else if ( choice.equals("Orange") ) {
				target.setForeground(Color.orange);
				rose.setColor( Color.orange );
			}
			else if ( choice.equals("Cyan") ) {
				target.setForeground(Color.cyan);
				rose.setColor( Color.cyan );
			}
			else if ( choice.equals("Pink") ) {
				target.setForeground(Color.pink);
				rose.setColor( Color.pink );
			}
			else if ( choice.equals("Magenta") ) {
				target.setForeground(Color.magenta);
				rose.setColor( Color.magenta );
			}
		}
		return true;
	}
} //end class RosePanel