//******************************************************************************
// Scramble.java:	Applet
//
//******************************************************************************
import java.applet.*;
import java.awt.*;
import java.awt.image.*;
import java.util.*;
import java.net.*;

//==============================================================================
// Main Class for applet Scramble
//
//==============================================================================

class CONST {
	static final String IMAGE= "caretaker32.jpg";
	static final int XLEN= 20;
	static final int YLEN= 20;
	static final int PREDELAY= 5000;
	static final int POSTDELAY= 5000;
	static final int INTERDELAY= 10;
	static final int SOUNDCOUNT= 3;
}

public class Scramble extends Applet implements Runnable
{	
	private MediaTracker tracker;
	private Image images[]= new Image[10];
	private int num_images;
	private Board board;
	Thread m_Scramble= null;
	private int xlen, ylen;
	private int predelay, postdelay, interdelay;
	public String audioname;
	public int audiocount;

	public void init() {
		String param[]= new String[10];
		int last, which, icount;		
		Random rand= new Random();

		xlen= getIntParam("Xlen", CONST.XLEN);
		ylen= getIntParam("Ylen", CONST.YLEN);
		predelay= getIntParam("Predelay", CONST.PREDELAY);
		postdelay= getIntParam("Postdelay", CONST.POSTDELAY);
		interdelay= getIntParam("Interdelay", CONST.INTERDELAY);
		audioname= getParameter("Soundfile");
		audiocount= getIntParam("Soundcount", CONST.SOUNDCOUNT);
		for (icount= 0; icount < 10; icount++) {
			param[icount]= getParameter("Image" + icount);
		
			if (param[icount] == null) {
				break;
			}
		}
		if (icount == 0) {
			param[icount++]= CONST.IMAGE;
		}

		tracker= new MediaTracker(this);

		num_images= 0;
		while (icount > 0) {
			which= Math.abs(rand.nextInt()) % icount;
			if (load_image(param[which], num_images)) {
				num_images++;
			}
			icount--;
			param[which]= param[icount];
		}
		setBackground(new Color(0, 0, 0));
	}

	public String getStringParam(String name, String defvalue) {
		String param= getParameter(name);
		return ((param == null) ? defvalue : param);
	}

	public int getIntParam(String name, int defvalue) {
		String param= getParameter(name);
		return ((param == null) ? defvalue : Integer.parseInt(param));
	}

	public boolean getBoolParam(String name, boolean defvalue) {
		  String param= getParameter(name);
		  return ((param == null) ? defvalue : Boolean.valueOf(param).booleanValue());
		  }


	public void paint(Graphics g) {
	}

	public void start() {
		if (m_Scramble == null) {
			m_Scramble = new Thread(this);
			m_Scramble.start();
		}
	}

	public void make_board(Image image)
	{
		int imgWidth= image.getWidth(this);
		int imgHeight= image.getHeight(this);
		int nPixels[]= new int[imgWidth * imgHeight];
		int col, row, max_cols, max_rows;
		SubImage subimage;
		Dimension d;
		int basex, basey;
		int spacex, spacey;
		int base_col, base_row;
		int pic_rows, pic_cols;
		
		PixelGrabber pg= new PixelGrabber(image, 0, 0, imgWidth, imgHeight, nPixels,
			0, imgWidth);
		try {
		   pg.grabPixels();
	   } catch (InterruptedException e) {
	   }
	   pic_cols= imgWidth / xlen; /* These round to nearest whole [XY]LEN */
	   pic_rows= imgHeight / ylen;
	   	  
	   d= size();
	   spacex= (d.width - pic_cols * xlen) / 2;
	   spacey= (d.height - pic_rows * ylen) / 2;

	   base_col= spacex / xlen;
	   base_row= spacey / ylen;

	   max_cols= pic_cols + 2 * base_col;
	   max_rows= pic_rows + 2 * base_row;

	   basex= (d.width - max_cols * xlen) / 2;
	   basey= (d.height - max_rows * ylen) / 2;
	 
	   board= new Board(max_cols, max_rows, xlen, ylen, getGraphics());  
	   for (row= 0; row < pic_rows; row++) {
		   for (col= 0; col < pic_cols; col++) {		
			   subimage= new SubImage(this, nPixels, imgWidth,
				   col * xlen, row * ylen, (base_col + col) * xlen,
				   (base_row + row) * ylen, xlen, ylen, basex, basey);

			   board.add_entry(subimage, base_col + col, base_row + row);
		   }
	   }
	}

	public boolean load_image(String urlstr, int ref)
	{
		try {
			images[ref]= getImage(new URL(getCodeBase(), urlstr));
		} catch (MalformedURLException e) {
			return (false);
		}
		if (images[ref] == null) {
			return (false);
		}
		tracker.addImage(images[ref], ref);
		return (true);
	}

	public void delay(int d)
	{ 
		try	{
				Thread.sleep(d);
			} catch (InterruptedException e) {
				stop();
			}
	}

	public void run() {
		int which= 0;
		int play_count= audiocount;

		AudioClip sound= null;
		if (audioname != null) {
			sound= getAudioClip(getCodeBase(), audioname);
		}

		while (true) {
			try {
				tracker.waitForID(which);
			} catch (InterruptedException e) {
				which= (which + 1) % num_images;
				continue;
			}
			
			if (sound!= null && play_count > 0) {
				sound.play();
				play_count--;
			}
			make_board(images[which]);
			board.digi_in();
			delay(predelay);

			while (board.swapper_move(getGraphics()) > 0) {
				delay(interdelay);
			}

			delay(postdelay);
			which= (which + 1) % num_images;
		} 
	}
}

class SubImage {
	public Image image;
	public int init_offx, init_offy, offx, offy, width, height, basex, basey;
	Graphics g;
	Component c;

	SubImage(Component _c, int pixels[],
			int scan, int poffx, int poffy, int _offx, int _offy, int _width, int _height,
			int _basex, int _basey) {
		
		image= _c.createImage(new MemoryImageSource(_width, _height,
			pixels, poffy * scan + poffx, scan));
		offx= -1;
		offy= -1;
		init_offx= _offx;
		init_offy= _offy;
		width= _width;
		height= _height;
		basex= _basex;
		basey= _basey;
		c= _c;
		g= _c.getGraphics();
	}

	public void set_coors(int _offx, int _offy, boolean clear_old) {
		if (clear_old && offx >= 0 && offy >= 0) {
			g.clearRect(offx + basex, offy + basey, width, height);
		}
		offx= _offx;
		offy= _offy;
		g.drawImage(image, offx + basex, offy + basey, null);
	}

	public void clear_coors(boolean clear_old) {
		if (clear_old && offx >= 0 && offy >= 0) {
			g.clearRect(offx + basex, offy + basey, width, height);
		}
		offx= -1;
		offy= -1;
	}
}

class Board {
	int max_cols;
	int max_rows;
	int xlen;
	int ylen;
	int width;
	int height;
	BoardEnt entries[];
	BoardEnt onboard[];
	int number;
	int max_num;
	Random rand;

	Board(int _max_cols, int _max_rows, int _xlen, int _ylen, Graphics g) {
		max_cols= _max_cols;
		max_rows= _max_rows;
		xlen= _xlen;
		ylen= _ylen;
		number= 0;
	
		onboard= new BoardEnt[max_cols * max_rows];
		entries= new BoardEnt[max_cols * max_rows];
		max_num= 0;
		number= 0;
		rand= new Random();
	}

	public int get_index(int col, int row) {
		return (col + row * max_cols);
	}

	public void update_entry(BoardEnt be, boolean clear_old) {
		int realx= be.col * xlen;
		int realy= be.row * ylen;
		be.image.set_coors(realx, realy, clear_old);
	}

	public void clear_entry(BoardEnt be, boolean clear_old) {
		be.image.clear_coors(clear_old);
	}

	public void add_entry(SubImage image, int col, int row) {
		BoardEnt be= new BoardEnt(image, col, row);
		entries[get_index(col, row)]= be;
		onboard[max_num++]= be;
		number++;
	}

	public void digi_in() {
		int anum= max_num;
		BoardEnt be;
		int i, which;

		while (anum > 0) {
			which= Math.abs(rand.nextInt()) % anum;
			be= onboard[which];

			update_entry(be, false);

			anum--;
			onboard[which]= onboard[anum];
			onboard[anum]= be;
		}
		number= max_num;
	}

	public void del_entry(int col, int row) {
		int index= get_index(col,row);
		BoardEnt be= entries[index];
		int i;

		if (be == null) {
			return;
		}
	
		entries[index]= null;
		for (i= 0; i < number; i++) {
			if (onboard[i] == be) {
				break;
			}
		}
		if (i < number) {
			number--;
			onboard[i]= onboard[number];
			onboard[number]= be;
		}

		clear_entry(be, true);
	}
		
	public int swapper_move(Graphics g) {
		int which= Math.abs(rand.nextInt()) % number;
		int direc= Math.abs(rand.nextInt()) % 4;
		BoardEnt be;
		int r1, c1, r2, c2;

		r1= 0; c1= 0; r2= 0; c2= 0;

		be= onboard[which];
		c1= be.col;
		r1= be.row;
		
		switch (direc) {
		case 0:
				c2= c1 + 1;
				r2= r1;
				break;
		case 1: 
				c2= c1 - 1;
				r2= r1;
				break;
		case 2:
				c2= c1;
				r2= r1 + 1;
				break;
		case 3:
				c2= c1;
				r2= r1 - 1;
				break;
		}
		if (c2 < 0 || c2 >= max_cols || r2 < 0 || r2 >= max_rows) {
			del_entry(c1, r1);
		} else {
			swap(c1, r1, c2, r2);
		}
		return (number);
	}

	public void swap(int c1, int r1, int c2, int r2) {
		int index1= get_index(c1, r1);
		int index2= get_index(c2, r2);
		
		BoardEnt e1= entries[index1];
		BoardEnt e2= entries[index2];

		if (e1 == null || e2 == null) {
			if (e1 == null && e2 == null) {
				
				return;
			}
			if (e2 == null) {
				e1.col= c2;
				e1.row= r2;
				entries[index1]= null;
				entries[index2]= e1;

				update_entry(e1, true);
			} else {
				e2.col= c1;
				e2.row= r1;
				entries[index1]= e2;
				entries[index2]= null;

				update_entry(e2, true);
			}
		} else {
			
			e1.col= c2;
			e1.row= r2;
			e2.col= c1;
			e2.row= r1;

			entries[index1]= e2;
			entries[index2]= e1;

			update_entry(e1, false);
			update_entry(e2, false);	
		}
	}
}


class BoardEnt {
	int row;
	int col;
	SubImage image;

	BoardEnt(SubImage _image, int _col, int _row) {
		image= _image;
		col= _col;
		row= _row;
	}
}

