import java.awt.*;
import java.awt.event.*;
import java.applet.*;
import java.math.*;
				
public class Applet1 extends Applet implements ActionListener,ItemListener
{
	Puzzle puzzle;
	Button restart,fullPic;
	boolean isFullPic;
	Choice dim;
	AudioClip whoo;
			
		
	private class Puzzle extends Canvas implements MouseListener,MouseMotionListener {
		Image image;
		int xSize,ySize;        // defines no. of parts in puzzle
		int xPart,yPart;        // defines size in pixels of each part
		int last_x,last_y;
		PuzzleItem selected_item;  // the index of the item that is selected
		PuzzleFrame frame;
				
			
		public void init() {
			this.setCursor(new Cursor(Cursor.HAND_CURSOR));
			xSize=ySize=Integer.parseInt(dim.getSelectedItem());
			this.addMouseListener(this);
			this.addMouseMotionListener(this);
					
			xPart = image.getWidth(this)/xSize;
			yPart = image.getHeight(this)/ySize;
			this.resize (xPart*xSize+5,ySize*yPart+5);
			frame=new PuzzleFrame();
		}
	
		public void mousePressed(MouseEvent e) {
			int i;
		
			selected_item=null;
			last_x=e.getX();
			last_y=e.getY();
			for (i=0;i<xSize*ySize;i++) {
				if (frame.items[i].isPointOnMe(last_x,last_y)) {
					selected_item=frame.items[i];
					break;
				}
			}
		}
		
		
		public void mouseReleased(MouseEvent e) {
			if (selected_item==null || selected_item==frame.blankItem) return;
			if (!frame.blankItem.isPointOnMe(e.getX(),e.getY()) || !frame.checkIfMoveLegal()) {
				selected_item.changeIndex(selected_item.frameIndex);
			return;
			}
			// move is legal
			int ind=frame.blankItem.frameIndex;
			frame.blankItem.changeIndex(selected_item.frameIndex);
			selected_item.changeIndex(ind);
			repaint();
			checkIfFinished();
		}
	
		public void mouseDragged(MouseEvent e) {;}
		public void mouseClicked(MouseEvent e) {;}
		public void mouseEntered(MouseEvent e) {;}
		public void mouseExited(MouseEvent e) {;}
		public void mouseMoved(MouseEvent e) {;}
				
	
		public void paint(Graphics g) {
			update(g);
		}
	
		public void update(Graphics g) {
		if (!isFullPic)
			frame.paint(g);
		else
			g.drawImage(puzzle.image,0,0,this);
		}	
	
		void checkIfFinished() {
			boolean finished=true;
			int i;
			
			for (i=0;i<xSize*ySize;i++) {
				if (frame.blankItem.frameIndex==i || frame.items[i].frameIndex==i)
					continue;
				else {
					finished=false;
					break;
				}
			}
			if (finished) {
				isFullPic=true;
				whoo.play();
				repaint();
			}
		}
			
		
		int[] IndexToCoords(int ind) {
			int xInd=ind%xSize;
			int yInd=ind/xSize;
			int[] c=new int[4];
			
			c[0]=xInd*xPart;
			c[2]=(xInd+1)*xPart;
			c[1]=yInd*yPart;
			c[3]=(yInd+1)*yPart;
			
			return c;
		}
		

		private class PuzzleFrame {
			int line_width;
			PuzzleItem []items;
			PuzzleItem blankItem;
			
			PuzzleFrame() {
				int i,j;
				int []r=createRandomPlacements();
				items=new PuzzleItem[xSize*ySize];
				int ind,x1,x2,y1,y2;
				boolean isBlank;
				for (i=0;i<ySize;i++) {
					for (j=0;j<xSize;j++) {
						ind=r[i*xSize+j];
						isBlank=(i+j==0);
						items[i*xSize+j]=new PuzzleItem(ind,j*xPart,i*yPart,
														(j+1)*xPart,(i+1)*yPart,isBlank);
						if (isBlank) 
							blankItem=items[i*xSize+j];
					}
				}
					
			}
			
			
			boolean checkIfMoveLegal() {
				int bx=blankItem.frameIndex % xSize;
				int by=blankItem.frameIndex / xSize;
				int sx=selected_item.frameIndex % xSize;
				int sy=selected_item.frameIndex / xSize;
				boolean x=(Math.abs(sx-bx)==1 && sy==by);
				boolean y=(Math.abs(sy-by)==1 && sx==bx);
				
				return (x || y || (Math.abs(sx-bx)==1 && Math.abs(sy-by)==1));
			}
			
			
			int[] createRandomPlacements() {
				int i,j,k,itemsLeft;
				double r;
				int []placements=new int[xSize*ySize];
				for (i=0;i<xSize*ySize;i++)
					placements[i]=-1;
				
				for (i=0;i<xSize*ySize;i++) {
					itemsLeft=xSize*ySize-i;       // no. of items left to randomize
					r=Math.random()*(itemsLeft);
					k=0;
					if (r==1)
						r-=0.0001;
					for (j=0;j<xSize*ySize;j++) {
						if (placements[j]!=-1) continue;
						if (k==(int)r) {
							placements[j]=i;
							break;
						}
						k++;
					}
				}
				return placements;
			}
						
			
			public void paint(Graphics g) {
				int i,j;
				g.clearRect(0,0,xPart*(xSize+1),yPart*(ySize+1));	
				for (i=0;i<ySize*xSize;i++)
					items[i].paint(g);
				
				g.setColor(new Color(0,0,0));
				
				for (i=0;i<ySize;i++) {
					for (j=0;j<xSize;j++) {
						g.drawRect(j*xPart,i*yPart,xPart,yPart);
					}
				}
			}
		}
						
		private class PuzzleItem {
			int []coords;
			boolean isBlank;
			int frameIndex;
			
			PuzzleItem(int f_ind,int sx1,
					int sy1,int sx2,int sy2,boolean blank) {
				coords=new int[8];
				changeIndex(f_ind);
				coords[4]=sx1;
				coords[5]=sy1;
				coords[6]=sx2;
				coords[7]=sy2;
				isBlank=blank;
			}
			
			void changeIndex(int newInd) {
				frameIndex=newInd;
				int[] c=IndexToCoords(newInd);
				int i;
				for (i=0;i<4;i++)
					coords[i]=c[i];
			}
				
			
			void paint(Graphics g) {
				if (!isBlank)
					g.drawImage(image,coords[0],coords[1],coords[2],
								coords[3],coords[4],coords[5],coords[6],coords[7],null);
				else 
					g.clearRect(coords[0],coords[1],xPart,yPart);
			}
			
		
			boolean isPointOnMe(int x, int y) {
				return ( (x>=coords[0] && x<=coords[2]) && (y>=coords[1] && y<=coords[3]) );
			}
		}       // end of item definition		
	}// end of puzzle definition


	public void init()
	{
		try {
		this.setBackground(Color.white);
		MediaTracker mt;
		puzzle=new Puzzle();
		mt = new MediaTracker(this);
		puzzle.image = getImage(getCodeBase(),"raft.gif");	
		whoo=getAudioClip(getCodeBase(),"whoo.au");
		mt.addImage(puzzle.image,0);
		try {
			mt.waitForID(0);
		} catch (java.lang.InterruptedException e) {
			System.out.println("Couldn't load the image");
		}
		
		dim=new Choice();
		int i;
		for (i=2;i<11;i++)
			dim.addItem(Integer.toString(i));
		dim.select(2);
		dim.addItemListener(this);
		isFullPic=false;
		resize(1000,1000);
		puzzle.init();
		this.add(puzzle);
		this.addMouseListener(puzzle);
		this.addMouseMotionListener(puzzle);
		restart=new Button("Restart");
		restart.addActionListener(this);
		fullPic=new Button("See full Picture");
		fullPic.addActionListener(this);
		this.setLayout(new FlowLayout());
		this.add(restart);
		this.add(fullPic);
		this.add(dim); }
		
		catch(Exception e) {System.out.println(e);}
		
	}
	
	public void paint(Graphics g) {
	}
	
	public void update(Graphics g) {
	}
	
	public void actionPerformed(ActionEvent e){
		if (e.getSource()==restart) {
			isFullPic=false;
			puzzle.init();
			puzzle.repaint();
		}
		else if (e.getSource()==fullPic) {
			isFullPic=true;
			puzzle.repaint();
		}
	} 
	public void itemStateChanged( ItemEvent e ) {
		isFullPic=false;
		puzzle.init();
		puzzle.repaint();
	}
}  // end of applet def



