import java.awt.*;
import java.awt.image.MemoryImageSource;
import java.applet.*;

public class Fract extends Applet implements Runnable {
	private int[] colors = new int[ 128 ];

	{
		for ( int i = 0; i < colors.length; i++ ){
			int r = 80 + i*i, g = i*i, b = 2*i;
			//int r = 2*i, g = 2*i, b = 100 + i*i;

			r = Math.min( 255, r );
			b = Math.min( 255, b );
			g = Math.min( 255, g );

			colors[ i ] = (0xFF000000 | (r<<16) | (g<<8) | b );//new Color( r, g, b );
		}
	}

	private float x = -2.1f, y = -1.5f;
	private float width = 3.0f, height = 3.0f;

	private Image offScrImage = null;
	private Graphics offScrGr = null;

	private MemoryImageSource imageSource;
	private int[] pixels;
	private Image image;

	private Dimension size;

	private boolean doRender = true;

	public void init(){
		try{
			size = getSize();
		}
		catch( Error e ){
			size = size();
		}

		pixels = new int[ size.width*size.height ];

		imageSource = new MemoryImageSource( size.width, size.height, pixels, 0, size.width );
		imageSource.setAnimated( true );
		imageSource.setFullBufferUpdates( true );

		image = createImage( imageSource );

		try{
			//System.out.println( "here" );
			Class c = Class.forName( "java.awt.event.MouseMotionListener" );
			//System.out.println( "here" );
			addMouseListener( new Mouse() );
			addMouseMotionListener( new Mouse() );
		}
		catch( Throwable t ){}

	}


	private Rectangle mouseArea = new Rectangle();
	private boolean mouseDown = false;
	int mouseX, mouseY;

	class Mouse implements java.awt.event.MouseListener, java.awt.event.MouseMotionListener {

		public void mouseClicked( java.awt.event.MouseEvent me ){}

		public void mousePressed( java.awt.event.MouseEvent me ){
			mouseDown = true;
			mouseX = mouseArea.x = me.getX();
			mouseY = mouseArea.y = me.getY();
			//repaint();
		}

		public void mouseMoved( java.awt.event.MouseEvent me ){}

		public void mouseDragged( java.awt.event.MouseEvent me ){
			mouseArea.x = mouseX;
			mouseArea.y = mouseY;
			mouseArea.width = me.getX() - mouseArea.x;
			mouseArea.height = me.getY() - mouseArea.y;
			if ( mouseArea.width < 0 ){
				mouseArea.x += mouseArea.width;
				mouseArea.width = -mouseArea.width;
			}

			if ( mouseArea.height < 0 ){
				mouseArea.y += mouseArea.height;
				mouseArea.height = -mouseArea.height;
			}
			repaint();
		}

		public void mouseReleased( java.awt.event.MouseEvent me ){
			mouseArea.x = mouseX;
			mouseArea.y = mouseY;
			mouseArea.width = me.getX() - mouseArea.x;
			mouseArea.height = me.getY() - mouseArea.y;

			if ( mouseArea.width < 0 ){
				mouseArea.x += mouseArea.width;
				mouseArea.width = -mouseArea.width;
			}

			if ( mouseArea.height < 0 ){
				mouseArea.y += mouseArea.height;
				mouseArea.height = -mouseArea.height;
			}
			mouseDown = false;
			calcNewArea();
		}

		public void mouseEntered( java.awt.event.MouseEvent me ){}
		public void mouseExited( java.awt.event.MouseEvent me ){}
	}

	private void calcNewArea(){

		if ( mouseArea.width == 0 || mouseArea.height == 0 ){
			x = -2.1f; y = -1.5f;
			width = 3.0f; height = 3.0f;
		}
		else {
			double relX = (mouseArea.x/((double)size.width));
			double relY = (mouseArea.y/((double)size.height));
			double relW = (mouseArea.width/((double)size.width));
			double relH = (mouseArea.height/((double)size.width));

			double newX = x + relX*width,
				  newY = y + relY*height,
				  newW = relW*width,
				  newH = relH*height;

			x = (float)newX;
			y = (float)newY;
			width = (float)newW;
			height = (float)newH;
		}

		doRender = true;
	}

	private int getColor( float x, float y ){
		float xn = x, yn = y;

		float lastVal = xn*xn + yn*yn;

		boolean done = false;

		int n = 0;

		int dir = 0;

		while (!done ){
			float xn1 = xn*xn + -yn*yn,
				  yn1 = 2*xn*yn;

			xn = xn1 +x;
			yn = yn1 +y;

			float thisVal = xn*xn + yn*yn;

			if ( thisVal > 4.0 )
				break;

			n++;

			if ( n > 126 )
				break;
		}

		/*for ( int i = 0; i < 10; i++ ){
			float xn1 = xn*xn + -yn*yn,
				  yn1 = 2*xn*yn;

			xn = xn1 +x;
			yn = yn1 +y;
		}*/

		//if ( (xn*xn + yn*yn) < 1 )
			//return Color.black;

		return colors[ n ];
	}

	private void setPixel( int x, int y, int color ){
		pixels[ size.width*y + x ] = color;
	}

	private void render(){
		int start = 0;
		int step = 4;

		while ( start < step ){

			for ( int i = start; i < size.width && !mouseDown; i+=step ){
				for ( int j = 0; j < size.height && !mouseDown; j++ ){
					int color =  getColor( (x + width*i/((float)size.width)), (y + height*j/((float)size.height)) );
					setPixel( i, j, color );
				}
			}
			imageSource.newPixels();
			repaint();
			start++;
			try{
				Thread.sleep( 20 );
			}
			catch( Exception e ){}
		}
	}


	private volatile boolean running = true;

	public void start(){
		running = true;
		(new Thread(this)).start();
	}

	public void run(){
		Thread.currentThread().setPriority( Thread.MIN_PRIORITY );

		while( running ){

			if ( doRender ){
				render();
				doRender = false;
			}
			try{
				Thread.sleep( 40 );
			}
			catch( Exception e ){}
		}

	}

	public void stop(){
		running = false;
	}


	private void initGr(){
		offScrImage = createImage( size.width, size.height );
		offScrGr = offScrImage.getGraphics();
	}

	public void update( Graphics g ){
		if ( offScrImage == null ) initGr();
		paint( offScrGr );
		g.drawImage( offScrImage, 0, 0, this );
	}

	public void paint( Graphics g ){
		//Thread.currentThread().setPriority( Thread.MIN_PRIORITY );



		//Dimension size = getSize();
		/*if (!mouseDown){

			int start = 0;
			int step = 10;

			while ( start < step ){

				for ( int i = start; i < size.width && !mouseDown; i+=step ){
					for ( int j = 0; j < size.height && !mouseDown; j++ ){
						offScrGr.setColor( getColor( (x + width*i/((float)size.width)), (y + height*j/((float)size.height)) ) );
						offScrGr.drawLine( i, j, i, j );
					}
				}
				g.drawImage( offScrImage, 0, 0, this );
				start++;
			}
		}*/
		//g.drawImage( offScrImage, 0, 0, this );
		g.drawImage( image, 0, 0, this );
		if (mouseDown) {

			g.setColor( Color.black );
			g.drawRect( mouseArea.x, mouseArea.y, mouseArea.width, mouseArea.height );
		}
	}


}
