// Joseph Bergin
// Pace University
// April, 1996
// Updated for Java 2 events April 2002

// Note that the sorts are artificially slowed to permit the process to be viewed
// more easily. 

import java.util.Random;
import java.lang.Math;
import java.awt.*;
import java.applet.*;
import java.awt.event.*;

class BubbleSorter implements Runnable
{	GSort g;
	
	public BubbleSorter(GSort s) { g = s; }
	
	public synchronized void run()
	{	g.bubbleSort();
	}	
}

class SelectionSorter implements Runnable
{	GSort g;	
	
	public SelectionSorter(GSort s) { g = s; }
	
	public synchronized void run()
	{	g.selectionSort();
	}
}

class QuickSorter implements Runnable
{	GSort g;	
	int first, last;
	
	public QuickSorter(GSort gs, int f, int l) { g = gs; first = f; last = l; }
	
	public synchronized void run()
	{	g.quickSort(first, last);
	}
}

public class GSort extends Applet
{	private static final int size = 400;
	private static final int m = 300;
	private static final int height = m + 50;
	private Random r = new Random();
	private Button randomArrayB;
	private Button nearlySortedB;
	private Button bubbleSortB;
	private Button selectionSortB;
	private Button quickSortB;
	private Graphics g;
	private Thread ActionThread = null;
	
	private int a[] = new int[size];
	private Color c[] = new Color[size];
	
	private void change(int i, int R)
	{	Color oldColor = g.getColor();
		g.setColor(this.getBackground());
		g.fillRect(30 + i, height+1-a[i], 2,2);
		g.setColor(c[i]);
		g.fillRect(30 + i, height + 1 - R, 2,2);
		a[i] = R;
		g.setColor(oldColor);
	}
	
	private void swap(int i, int j)
	{	int t = a[i];
		change(i, a[j]);
		change(j, t);
	}
	
	private void clear()
	{	int i;
		for(i = 0; i < size; ++i)
		{	g.clearRect(30+i, height+1-a[i], 2,2);
			a[i] = 0;
		}
	}
	
	private synchronized void randomArray()
	{	int i;
		clear();
		for(i = 0; i < size; ++i)
		{	a[i] = 0; 
			change(i, Math.abs(r.nextInt() ) % m + 1);
		}
	}
	
	private synchronized void nearlySortedArray()
	{	int i, j;
		clear();
		for(i = 0; i < size; ++i)
			a[i] = i / 4 * 3 ;
		for(i = 0; i < 5; ++i)
			for(j = 1; j < size; ++j)
			{	if(Math.abs(r.nextInt() ) % 2 == 0)
					swap(j-1, j);
				if(Math.abs(r.nextInt() ) % 100 == 0)
					swap(i, Math.abs(r.nextInt() ) % size );
			}
	}
	
	private void delay(int quantum)
	{ 	try
		{	Thread.sleep(quantum);
		}
		catch(InterruptedException i)
		{
		}
	}
	
	synchronized void bubbleSort() // obsolete
	// The largest so far on a pass is shown in red.
	{	int i, j;
		Color t;
		for(i = 0; i < size; ++i)
			for(j = 0; j < size - i - 1; ++j)
			{	if(a[j] > a[j+1])
				{	c[j] = Color.black;
					c[j+1] = Color.red;
					swap(j, j+1);
				}
			c[size - i - 1] = Color.black;
			change(size - i - 1, a[size - i - 1]);
			}
	}
	
	synchronized void selectionSort() // obsolete
	// The location of the smallest seen on an inner pass is in red.
	{	int i, j, small, s;
		for(i = 0; i< size -1; ++i)
		{	s = i;
			c[s] = Color.red;
			change(s, a[s]);
			small = a[s];
			for(j = i + 1; j < size; ++j)
			{	if(a[j] < small)
				{	c[s] = Color.black;
					change(s, a[s]);
					s = j;
					c[s] = Color.red;
					change(s, a[s]);
					small = a[s];
					delay(5);
				}
			}
			c[s] = Color.black;
			change(s, a[i]);
			c[i] = Color.black;
			change(i, small);
		}
	}
	
	synchronized void quickSort(int L, int U)
	{	int i, T, lastLow;
		int quantum = 5;
		if(L < U)
		{	T = a[L];
				lastLow = L;
			for(i = L + 1; i <= U; ++i)
				if(a[i] < T)
				{	lastLow++;
					swap(lastLow, i);
				}
			swap(L, lastLow);
			delay(quantum);
			quickSort(L, lastLow - 1);
			quickSort(lastLow + 1, U);
		}
	}
	
	class BubbleListener implements ActionListener
	{
		public void actionPerformed(ActionEvent e)
		{
			ActionThread = new Thread(new BubbleSorter(GSort.this));
			ActionThread.start();
		}
	}
	
	class SelectionListener implements ActionListener
	{
		public void actionPerformed(ActionEvent e)
		{
			ActionThread = new Thread(new SelectionSorter(GSort.this));
			ActionThread.start();		
		}
	}
	
	class QuickListener implements ActionListener
	{
		public void actionPerformed(ActionEvent e)
		{	ActionThread = new Thread(new QuickSorter(GSort.this, 0, size - 1));
			ActionThread.start();
		}
	}
	
	class RandomListener implements ActionListener
	{
		public void actionPerformed(ActionEvent e)
		{	randomArray();
		}
	}
	
	class NearlySortedListener implements ActionListener
	{
		public void actionPerformed(ActionEvent e)
		{  nearlySortedArray();
		}
	}
		
	public void init()
	{	
		setLayout(new BorderLayout());
		Label L = new Label("Click an array type, then a sort type.");
		L.setFont(new Font("Times", Font.BOLD, 12));
		add("North", L);
		Panel P = new Panel();
		randomArrayB = new Button("Random Array");
		randomArrayB.addActionListener(new RandomListener());
		nearlySortedB = new Button("Nearly Sorted Array");
		nearlySortedB.addActionListener(new NearlySortedListener());
		bubbleSortB = new Button("Bubble Sort");
		bubbleSortB.addActionListener(new BubbleListener());
		selectionSortB = new Button("Selection Sort");
		selectionSortB.addActionListener(new SelectionListener());
		quickSortB = new Button("Quick Sort");
		quickSortB.addActionListener(new QuickListener());
		P.add(randomArrayB);
		P.add(nearlySortedB);
		P.add(bubbleSortB);
		P.add(selectionSortB);
		P.add(quickSortB);
		add("South",P);
		
		int i;
		for(i = 0; i < size; ++i)
		{	a[i] = 0;
			c[i] = Color.black;
		}
		g  = getGraphics();
	}
	
	public void paint(Graphics g)
	{	for(int i = 0; i < size; ++i)
		{	Color oldColor = g.getColor();
			g.setColor(this.getBackground());
			g.fillRect(30 + i, height+1-a[i], 2,2);
			g.setColor(c[i]);
			g.fillRect(30 + i, height + 1 - a[i], 2,2);
			g.setColor(oldColor);
		}
	}

}