/**
* @(#)FadeTransition.java
* @version 1.51 04/06/97
* @author Robert Temple (robertt@starwave.com)
*/

import java.awt.*;
import java.awt.image.MemoryImageSource;

/**
* The FadeTransition class changes one image into another by drawing
* a set of random pixels from the new image onto the old image each cell.
* the number of pixels draw each cell is the same for each cell.
*
* DESIGN NOTE: This class uses a bunch of random number to fill in the pixels
*   of each cell.  If found Java's random number generator too slow for
*   this purpose, and wrote a really simple one.  The numbers are not very
*   random, but it is very fast compared to Sun's.  The numbers generated
*   are more then random enough for my purposes.  Also the it only generates
*   numbers between 0-7.  The only numbers we need
*
*/
public class FadeTransition extends BillTransition {

// Static Members
  
	/** 
	* The total number of cells this transition will show on the screen before
        * the new image is shown in its entirety
	*
	* DON'T CHANGE THIS NUMBER, the random number generator will not
	* work because it only produces number between 0-7
        */
	private static final int CELLS = 7;

	/** Used by a very-pseudo random number generator */
	private static final int MULTIPLIER = 0x5D1E2F;

	/**
	* Creates a random array used later to create cells.
	* The FadeTransition class uses a two dimensional array that holds indexes
	* a number of random numbers.  The first dimension is 8 elements in size,
	* one element for each Cell.  Each element in the second dimension is 1/8 
	* the size of 
	*/
	private static int[][] createRandomArray(int number_pixels, int cell_h) {
		int total_cells = CELLS + 1;

		int new_pixels_per_cell = number_pixels / total_cells;

		// A multidimensional array that hold indexes into the work pixel array
		// for every single pixel in the work pixel array.  The first dimension
		// Holds the pixels for each cell, the other dimension is the pixels
		// indicies.	
		int[][] random = new int[total_cells][new_pixels_per_cell];

		// every cell will have the same number of new pixels draw into
		// the image.  No more no less.  So keep track of the number
		// of random values added to each random cell, so that we don't
		// try to add too many.  The array below keeps count
		int random_count[] = new int[total_cells];
		for(int s = 0; s < total_cells; ++s) {
			random_count[s] = 0;
		}

		int cell;
		int rounded_new_pixels_per_cell = new_pixels_per_cell * total_cells;

		// inline random number generator starts here
		// *** read DESIGN NOTES above ***
		int seed = (int)System.currentTimeMillis();

		int denominator = 10;
		while((new_pixels_per_cell % denominator > 0 || cell_h % denominator == 0) && denominator > 1) {
			--denominator;
		}

		int new_randoms_per_cell = new_pixels_per_cell / denominator;
		int new_randoms = rounded_new_pixels_per_cell / denominator;

		// create a bunch of random numbers and put them into the
		// array without checking to see if any particular array
		// is full.   Do this until it is possible that one filled
		// up.
		for(int p = 0; p < new_randoms_per_cell; ++p) {
			// Generate a random number between 0 - 7
			seed *= MULTIPLIER;
			cell = (seed >>> 29);
			random[cell][random_count[cell]++] = p;
		}

		// might as well as mix up the random number generator a bit more
		seed += 0x5050;

		// give other threads a shot at the CPU
		try { Thread.sleep(150); } catch (InterruptedException e) {}

		// generate the rest of the random numbers
		for(int p = new_randoms_per_cell; p < new_randoms; ++p) {
			// Generate a random number between 0 - 7
			seed *= MULTIPLIER;
			cell = (seed >>> 29);

			// if the cell this number is supposed to go in is
			// full, put it in the next cell
			while(random_count[cell] >= new_randoms_per_cell) {
				if(++cell >= total_cells) {
					cell = 0;
				}
			}
			random[cell][random_count[cell]++] = p;
		}

		// we only actually filled up the arrays part of the way.
		// now fill them up the rest of the way using the numbers
		// we already generated.  Also, we don't need to fill in
		// the numbers for the last cell, since at the last cell
		// we know that all the work_pixels would have been filled
		// in with pixels from the new image anyways.
		for(int s = 0; s < CELLS; ++s) {

			for(int ps = new_randoms_per_cell; ps < new_pixels_per_cell;
						ps += new_randoms_per_cell) {

				int offset = ps * total_cells;

				for(int p = 0; p < new_randoms_per_cell; ++p) {
					random[s][ps + p] = random[s][p] + offset;
				}
			}

			// give other threads a shot at the CPU
			try { Thread.sleep(50); } catch (InterruptedException e) {}
		}

		// this cell is never actually used it is only needed previously
		// to make sure the random numbers where evenly distributed.
		random[CELLS] = null;

		return random;
	}

// Instance Members

	/**
	* Used to initialize the transition right after it is created.
	* creates cells
	* @param owner the component to be used to create images from cells
	*/
	public void init(Component owner, int[] current, int[] next) {
		init(owner, current, next, CELLS);

		// copy all of the current billboard's pixels into the work pixels
		System.arraycopy((Object)current_pixels, 0, (Object)work_pixels, 
						0, pixels_per_cell);

		// get the random array for this sized applet from the object table.
		int random[][] = (int[][])object_table.get(
						getClass().getName() + pixels_per_cell);

		// if the random array is not found, create it and put it in the 
		// object table.
		if(random == null) {
			random = createRandomArray(pixels_per_cell, cell_h);		
			object_table.put(getClass().getName() + pixels_per_cell, random);
		}

		// create all the image cells
		for(int c = 0; c < CELLS; ++c) {

			// give other threads a shot at the CPU
			try { Thread.sleep(100); } catch (InterruptedException e) {}

			// draw in the pixels that the random array specifies for
			// this cell from the new image into the work pixels
			int limit = random[c].length;
			for(int p = 0; p < limit; ++p) {
				int pixel_index = random[c][p];
				work_pixels[pixel_index] = next_pixels[pixel_index];
			}

			// give other threads a shot at the CPU
			try { Thread.sleep(50); } catch (InterruptedException e) {}

			// create the new cell image from the work pixels
			createCellFromWorkPixels(c);
		}

		// we don't need the work pixels anymore
		work_pixels = null;
	}
}
