import java.applet.*;
import java.awt.*;
import java.awt.image.*;

public class spinart extends Applet implements Runnable
{
	private Thread	 m_spinart = null;
	int nc,nch,cx=80,cy,cw,ci;
	int width,height,tx,ty,tw,th,tcx,tcy,tc,ax,ay,aw,ah,ac,acx,acy;
	int bufx,bufy,bufw,bufh,atx,aty;
	int helpx,helpy,helpw,helph;
	int bx=8,by=60,bw=100,bh=30,bsp=50,nb=4,bi=-1;
	int indx=8,indy=10,indw=20,indh=20;
	int mx,my,red,green,blue,tablered,tablegreen,tableblue;
	int x1,y1,x2,y2,ir1,ir2,den;
	double ta=0,dt=.2,dx,dy,r1,r2,da,cda,sda,ma;
	boolean mp=false;
	Color[] clr={new Color(255,0,0),new Color(255,192,203),new Color(140,0,0),
		   new Color(255,80,0),new Color(178,34,34),
		   new Color(0,255,0),new Color(164,255,164),new Color(0,164,0),
		   new Color(0,0,255),new Color(0,0,164),new Color(128,128,255),
		   new Color(65,105,225),new Color(196,196,255),new Color(255,255,0),
		   new Color(255,215,0),new Color(255,255,164),new Color(164,0,164),
		   new Color(255,0,255),new Color(255,128,255),new Color(0,255,255),
		   new Color(164,255,255),new Color(0,164,164),new Color(255,128,64),
		   new Color(196,92,50),new Color(0,0,0),new Color(255,255,255)};
	Color bgcolor=new Color(255,255,255);
	Color tablecolor=new Color(224,224,224);
	Color[] buttonclr={new Color(192,192,192), new Color(255,255,255), new Color(80,80,80)};
	Image buf,art,spin;
	Graphics bg,ag,gg,sg;
	FontMetrics fm;
	int[] srred,srgreen,srblue;
	String[] buttontext={"Spin","Stop","Clear","Help"};
	boolean spinflag=false,helpflag=false,erasehelpflag=false,rpflag=false;
	String[] helpstr={"A square paper card is placed on a circular turntable.",
		"The turntable is made to spin by pressing the Spin",
		"button and stopped by pressing the Stop button.",
		"When spinning, paint can be squirted onto the card by",
		"placing the mouse over it and holding down the",
		"mouse button. The paint color is selected by clicking",
		"on the color bar.\n\n",
		"After squirting various paint colors onto the card, the",
		"result can be viewed by stopping the turntable. More",
		"colors can be added by spinning the turntable again.",
		"New artwork can be started by pressing the Clear",
		"button."};

	public spinart()
	{
	}

	public String getAppletInfo()
	{
		return "Name: spinart\r\n" +
		       "Author: Bob Roesser\r\n";
	}


	public void init()
	{
		width=Integer.parseInt(getParameter("width"),10);
		height=Integer.parseInt(getParameter("height"),10);
		th=height-20;tw=th;
		tx=width-tw-10;ty=10;
		tc=tw/2;
		tcx=tx+tc;tcy=ty+tc;
		ah=(int)((double)th*Math.cos(Math.PI/4));aw=ah;
		ax=width-height+height/2-aw/2;ay=height/2-ah/2;
		ac=aw/2;
		den=tc-ac;
		bufx=tx;bufy=ty;bufw=tw+2;bufh=th+2;
		atx=ax-tx;aty=ay-ty;
		helpx=tx+10;
		helpy=ty+16;
		helpw=tw-20;
		helph=th;
		tablered=tablecolor.getRed();
		tablegreen=tablecolor.getGreen();
		tableblue=tablecolor.getBlue();
		srred=new int[tc];
		srgreen=new int[tc];
		srblue=new int[tc];
		gg=getGraphics();
		gg.setFont(new Font("TimesRoman",Font.BOLD,14));
		fm=gg.getFontMetrics();
		art=createImage(aw,ah);
		ag=art.getGraphics();
		spin=createImage(tw,th);
//		sg=spin.getGraphics();
		buf=createImage(bufw,bufh);
		bg=buf.getGraphics();
		nc=clr.length;
		nch=(nc+1)/2;
		cw=(height-2*ty)/nch;
		cy=height-ty;
		bw=cx-2*bx;
		indw=bw;indh=cw;indy=height-ty-nch*cw;
		resize(width,height);
		clear();

	}

	public void destroy()
	{
	}

	 public void palette(){
// draw palette
	for(int i=0;i<nc;i++){
		int ih=i/nch;
		int il=i%nch;
		gg.setColor(clr[i]);
		gg.fillRect(cx+ih*cw,cy-(il+1)*cw,cw,cw);}
		gg.setColor(Color.black);
		gg.drawRect(cx-1,cy-1-nch*cw,2*cw,nch*cw);
  }

	 public void drawbuttons(){
		int sh=fm.getHeight();
		for(int i=0;i<nb;i++){
			gg.setColor(buttonclr[0]);
			gg.fillRect(bx,by+i*bsp,bw,bh);
			gg.setColor(Color.black);
			gg.drawRect(bx,by+i*bsp,bw,bh);
			int sw=fm.stringWidth(buttontext[i]);
			gg.drawString(buttontext[i],bx+bw/2-sw/2,by-2+i*bsp+bh/2+sh/2);
			}
		buttonhighlight();
	}


	public void buttonhighlight(){
		int bc;
		for(int i=0;i<nb;i++){
			bc=1;
			if(bi==i)bc=3-bc;
			gg.setColor(buttonclr[bc]);
			gg.drawLine(bx,by+i*bsp,bx,by+i*bsp+bh-1);
			gg.drawLine(bx,by+i*bsp,bx+bw-1,by+i*bsp);
			gg.setColor(buttonclr[3-bc]);
			gg.drawLine(bx+bw-1,by+i*bsp+bh-1,bx+bw-1,by+i*bsp);
			gg.drawLine(bx+bw-1,by+i*bsp+bh-1,bx,by+i*bsp+bh-1);}
	}

	public void indicator(){
			if(ci>=0)gg.setColor(clr[ci]);
			else gg.setColor(Color.lightGray);
			gg.fillRect(indx,indy,indw,indh);
			gg.setColor(Color.black);
			gg.drawRect(indx-1,indy-1,indw,indh);
		}

	public void help(){
			int i=0,ii,j1=0,j2=0,j3,ibeg=0,jbeg=0,iend=0,jend=0;
			int strwidth=0,ww=0,spacew,strheight,hc=helpy,maxlines,nlines=0;
			String line,wrd;
			boolean done=false,almostdone=false,endl=false,endlprev=false;
			helpflag=true;
			Font oldfont=gg.getFont();
			gg.setFont(new Font("TimesRoman",Font.BOLD,16));
			FontMetrics hfm=gg.getFontMetrics();
			spacew=hfm.stringWidth(" ");
			strheight=hfm.getHeight();
			maxlines=helph/strheight;
			gg.setColor(Color.white);
			gg.fillRect(tx,ty,tw+1,th+1);
			gg.setColor(Color.black);
			while(!done){
				if(!almostdone){
					j2=helpstr[i].indexOf(" ",j1);
					j3=helpstr[i].indexOf("\n",j1);
					if(j2<0)j2=helpstr[i].length();
					if(j3>=0&&j3<j2){
						j2=j3;
						endl=true;}
					wrd=helpstr[i].substring(j1,j2);
					ww=hfm.stringWidth(wrd);}
				else j1=j2+1;
				if((strwidth+spacew+ww)>=helpw||endlprev||almostdone){
					if(ibeg<iend){
						line=helpstr[ibeg].substring(jbeg)+" ";
						for(ii=ibeg+1;ii<iend;ii++)line+=helpstr[ii]+" ";
						if(jend>0)line+=helpstr[iend].substring(0,jend-1);}
					else line=helpstr[iend].substring(jbeg,jend-1);
					gg.drawString(line,helpx,hc);
					if(almostdone)break;
					strwidth=0;
					hc+=strheight;
					ibeg=i;
					jbeg=j1;
					if(nlines++>=maxlines-1)break;}
				strwidth+=spacew+ww;
				j1=j2+1;
				endlprev=endl;
				endl=false;
				done=almostdone;
				iend=i;
				jend=j1;
				if(j2>=helpstr[i].length()-1){
					if(i<helpstr.length-1){
						i++;
						j1=0;}
					else almostdone=true;}
			}
			gg.setFont(oldfont);
		}

	public void startspin(){
		spinflag=true;
		rpflag=true;
		}

	public void stopspin(){
		spinflag=false;
		rpflag=true;
		}

	public void clear(){
		ag.setColor(Color.white);
		ag.fillRect(0,0,aw,ah);
		sg=spin.getGraphics();
		sg.setColor(bgcolor);
		sg.fillRect(0,0,tw,th);
		sg.setColor(tablecolor);
		sg.fillOval(0,0,tw,th);
		sg.setColor(Color.white);
		sg.fillOval(tc-aw/2,tc-ah/2,aw,ah);
		for(int i=0;i<tc;i++){
			if(i<ac){
				red=255;green=255;blue=255;}
			else {
				int num=i-ac;
				red=255-(255-tablered)*num/den;
				green=255-(255-tablegreen)*num/den;
				blue=255-(255-tableblue)*num/den;
				sg.setColor(new Color(red,green,blue));
				sg.drawOval(tc-i,tc-i,2*i,2*i);}
			srred[i]=red;
			srgreen[i]=green;
			srblue[i]=blue;}
		helpflag=false;
		rpflag=true;
		}

	public void update(Graphics g){
		paint(g);
		}

	public void paint(Graphics g)
	{
		g.setColor(bgcolor);
		g.fillRect(0,0,width,height);
// draw palette
		palette();
// draw buttons
		drawbuttons();
// draw indicator
		indicator();
// paint art or help
		drawart();
	}

	public void drawart(){
// paint art or help
		if(helpflag)help();
		else{
			if(erasehelpflag){
				bg.setColor(bgcolor);
				bg.fillRect(0,0,bufw,bufh);}
			erasehelpflag=false;
			bg.setColor(tablecolor);
			bg.fillOval(0,0,tw,th);
// show spinning or stationary art
			if(spinflag&&spin!=null)bg.drawImage(spin,0,0,this);
			if(!spinflag&&art!=null)bg.drawImage(art,atx,aty,this);
			bg.setColor(Color.black);
			bg.drawOval(0,0,tw,th);}
		gg.drawImage(buf,bufx,bufy,this);
	}


	public void start()
	{
		if (m_spinart == null)
		{
			m_spinart = new Thread(this);
			m_spinart.start();
		}
	}
	
	public void stop()
	{
		if (m_spinart != null)
		{
			m_spinart.stop();
			m_spinart = null;
		}
	}



	public void run()
	{
		while (true)
		{
			try
			{
				if(mp&&spinflag){
					dx=(double)(mx-tcx);
					dy=(double)(my-tcy);
					ma=Math.atan2(dy,dx);
					r1=Math.sqrt(dx*dx+dy*dy);
					da=ma-ta;
					r2=2*r1;
					cda=Math.cos(da);
					sda=Math.sin(da);
					x1=(int)(r1*cda)+ac;
					y1=(int)(r1*sda)+ac;
					x2=(int)(r2*cda)+ac;
					y2=(int)(r2*sda)+ac;
// draw splotch on art image
					ag.setColor(clr[ci]);
					ag.fillOval(x1-4,y1-4,8,8);
					for(int i=-2;i<=2;i++)for(int j=-1;j<2;j+=2)
						ag.drawLine(x1+i,y1+j*i,x2,y2);
// average color with previous spin radius colors and draw circles on spin image
					ir1=(int)r1;
					ir2=(int)r2;
					if(ir2>tc)ir2=tc;
					red=clr[ci].getRed();
					green=clr[ci].getGreen();
					blue=clr[ci].getBlue();
					int imin=Math.max(0,ir1-3);
					int k=Math.max(7,ir1/4);
					int k2=4*k;
					sg=spin.getGraphics();
					for(int i=imin;i<ir2;i++){
						if(i>ir1+3)k=k2;
						srred[i]=(k*srred[i]+red)/(k+1);
						srgreen[i]=(k*srgreen[i]+green)/(k+1);
						srblue[i]=(k*srblue[i]+blue)/(k+1);
						sg.setColor(new Color(srred[i],srgreen[i],srblue[i]));
						sg.drawOval(tc-i,tc-i,2*i,2*i);}
					sg=null;
				rpflag=true;;}
				if(rpflag)drawart();
				rpflag=false;
// increment turntable angle
				ta+=dt;

				Thread.sleep(50);
  }
			catch (InterruptedException e)
			{
				stop();
			}
		}
	}

	public boolean mouseDown(Event evt, int x, int y)
	{
		if(helpflag)erasehelpflag=true;
		helpflag=false;
// check buttons
		bi=-1;
		if(x>=bx&&x<=bx+bw&&y>=by+0*bsp&&y<=by+0*bsp+bh){startspin();bi=0;}
		if(x>=bx&&x<=bx+bw&&y>=by+1*bsp&&y<=by+1*bsp+bh){stopspin();bi=1;}
		if(x>=bx&&x<=bx+bw&&y>=by+2*bsp&&y<=by+2*bsp+bh){clear();bi=2;}
		if(x>=bx&&x<=bx+bw&&y>=by+3*bsp&&y<=by+3*bsp+bh){help();bi=3;}
		if(bi>=0)buttonhighlight();
// check color selection
		if(x>=cx&&x<=cx+2*cw&&y>=cy-nch*cw&&y<=cy){
			ci=nch*((x-cx)/cw)+(cy-y)/cw;
			indicator();}
// check whether over turntable
		if(x>tx&&x<tx+tw&&y>ty&&y<ty+th){
			mx=x;
			my=y;
			mp=true;}
		return true;
	}

	public boolean mouseUp(Event evt, int x, int y)
	{
		if(bi>=0){bi=-1;buttonhighlight();}
		mp=false;
		return true;
	}

	public boolean mouseDrag(Event evt, int x, int y)
	{
// check whether over turntable
		if(x>tx&&x<tx+tw&&y>ty&&y<ty+th){
			mx=x;
			my=y;
			mp=true;}
		else mp=false;
		return true;
	}

	public boolean mouseMove(Event evt, int x, int y)
	{
		return true;
	}

}
