//---------------------------------
// SortPanel.Java
// Written By: Russell Schwager
//             russells@jhu.edu
// February 10, 1997
//---------------------------------

// Insert Copyright comments

//---BEGIN UI CODE---------
import java.awt.*;
import java.applet.*;
import java.util.Date;
import java.util.Random;

class SortPanel extends Panel implements Runnable
{

    Image 		offscreenImage;		// Used for smooth animation
    Graphics 	offscreenGraphics;	// Used for smooth animation
	SortableObject sObject;			// Object to be sorted
	SortableObject preSortObject;	// Object before sorting
	SortAlgorithm  sAlgorithm;
	Date		date;
	Random		random;
    public static final int SIZE = 20; 
	public static final int STRINGSIZE = 5; 
	public static final int SLEEPTIME = 1000; 
	int			buttonDown;
	int			topIndex1, topIndex2, botIndex1, botIndex2;
	boolean		stopped, mouseDown, startSort;
	Thread		sortThread;

	public SortPanel(Image offscreenImage, Graphics offscreenGraphics)
    { // constructor
      
		this.offscreenImage = offscreenImage;
		this.offscreenGraphics = offscreenGraphics;

		this.setBackground(Color.white);

		// Random generator
		date = new Date();
		random = new Random(date.getTime());

		stopped = true;
		mouseDown = false;
		startSort = false;
		buttonDown = 0;
	}

	public void paint(Graphics g)
	{

        // figure out the dimension of the panel
        Rectangle border = bounds();

		// Clear the screen
        offscreenGraphics.clearRect(0, 0, this.size().width,
                                          this.size().height);
		
		// Draw a border around the panel
		offscreenGraphics.setColor(Color.gray);
		offscreenGraphics.drawRect(0, 0, border.width - 1, 
			border.height - 1);
	
		// Draw unsorted object/array
		offscreenGraphics.setColor(Color.blue);
		offscreenGraphics.drawString("Unsorted Collection: ", 
			10, 125);

		offscreenGraphics.setColor(Color.red);
		if (stopped)
			offscreenGraphics.drawString("Press Start To Begin Demo", 
			10, 150);
		else
		{
			for (int i = topIndex1; i < botIndex1; i++)
			{
				offscreenGraphics.drawString(preSortObject.toString(i), 
						40, 140 + ((i - topIndex1) * 12));
			}
			
			if (mouseDown && buttonDown == 1)
				drawButtons(offscreenGraphics, 10, 130, 
					false, true, 1);
			else if (mouseDown && buttonDown == 2)
				drawButtons(offscreenGraphics, 10, 130, 
					true, false, 1);
			else 
				drawButtons(offscreenGraphics, 10, 130, 
					true, true, 1);
		}

		// Draw Sort Method
		offscreenGraphics.setColor(Color.blue);
		offscreenGraphics.drawString("Sort Method: ", 215, 125);

		offscreenGraphics.setColor(Color.red);
		if (!stopped)
			offscreenGraphics.drawString(sAlgorithm.getAlgorithmName(), 215, 140);
		else
			offscreenGraphics.drawString("None", 215, 150);

		// Draw unsorted object/array
		offscreenGraphics.setColor(Color.blue);
		offscreenGraphics.drawString("Sorted Collection: ", 360, 125);
		
		offscreenGraphics.setColor(Color.red);
		if (stopped)
			offscreenGraphics.drawString("Press Start To Begin Demo", 360, 150);
		else
		{
			for (int i = topIndex2; i < botIndex2; i++)
			{ // evenly space out s
				offscreenGraphics.drawString(sObject.toString(i), 
							390, 140 + ((i - topIndex2) * 12));
			}
			
			if (mouseDown && buttonDown == 3)
				drawButtons(offscreenGraphics, 360, 130, 
					false, true, 2);
			else if (mouseDown && buttonDown == 4)
				drawButtons(offscreenGraphics, 360, 130,
					true, false, 2);
			else
				drawButtons(offscreenGraphics, 360, 130, 
					true, true, 2);
		}

		if (!stopped)
		{
			drawSObject(offscreenGraphics);
			sAlgorithm.drawMiscInfo(offscreenGraphics);
		}

		g.drawImage(offscreenImage, 0, 0, this);

    }

    public void update(Graphics g)
    {
        paint(g);
    }

    /*
     * Run the sorting algorithm. This method is
     * called by class Thread once the sorting algorithm
     * is started.
     */
	public void run() 
	{

		try 
		{
			sAlgorithm.demoSort();
		} 
		catch(Exception e) 
		{
		}

    }

    
	public synchronized void stop() 
	{ // Stops the sorting algorithm
		
		if (sortThread != null) 
		{
			try 
			{
				sortThread.stop();
			} 
			catch (IllegalThreadStateException e) 
			{
				// ignore this exception
			}
			sortThread = null;
		}
		
		if (sAlgorithm != null)
		{
			try 
			{
				sAlgorithm.stop();
			} 
			catch (IllegalThreadStateException e) 
			{
				// ignore this exception
			}
		}
	}

	/* Prevents several sorts from starting if the user hits start
	   over and over.  It's synchronized because start and stop
	   work on the same thread variable */
    private synchronized void startSort() 
	{ 
        if (sortThread == null || !sortThread.isAlive()) 
		{
            repaint();
            sortThread = new Thread(this);
            sortThread.start();
        }
    }

    public void pause() 
	{

        if (sortThread != null) 
            repaint();

        try 
		{
			Thread.sleep(20);
		} 
		catch (InterruptedException e)
		{
		}
    }


	public void sleep()
	{ // Sleep for SLEEPTIME ms
		try
		{
			Thread.sleep(this.SLEEPTIME);
		}
		catch(InterruptedException e)
		{
			System.err.println(e.toString());
		}
	}

	private void drawSObject(Graphics g)
	{ // draws the sortable object out in "bars".

		boolean done[] = new boolean[this.SIZE];
		int largest, i = 0, j = 0, start;

		for (i = 0; i < this.SIZE; i++)
			done[i] = false;

		g.setColor(Color.gray);
		i = this.SIZE - 1;
		while (i >= 0)
		{
			start = 0;
			while (start < this.SIZE - 1 && done[start] == true)
				start++;

			largest = start;

			for (j = 0; j < this.SIZE; j++)
			{
				if (done[j] == false && start != j && 
					sObject.at(j) == sObject.compare(j, largest))
					largest = j;
			}

			done[largest] = true;
			g.fillRect(100 + (15 * largest), 100 - (i * 4), 10, (i + 1) * 4);
			i--;
		}
	}

	private void drawButtons(Graphics g, int x, int y, 
		boolean bDown1, boolean bDown2, int column)
	{ // draw "fake" scroll bar buttons

		// Draw the top button if there are elements above
		// what is seen.
		if ((column == 1 && topIndex1 != 0) ||
			(column == 2 && topIndex2 != 0))
		{
			g.setColor(Color.lightGray);
			g.fill3DRect(x, y, 10, 10, bDown1);
			
			g.setColor(Color.black);
			int x1[] = { x + 2, x + 5, x + 8 };
			int y1[] = { y + 8, y + 2, y + 8 };
			g.fillPolygon(x1, y1, 3);
		}
		
		// Draw the bottom button if there are elements below
		// what is seen.
		if ((column == 1 && botIndex1 != this.SIZE - 1) ||
			(column == 2 && botIndex2 != this.SIZE - 1))
		{
			g.setColor(Color.lightGray);
			g.fill3DRect(x, y + 50, 10, 10, bDown2);

			g.setColor(Color.black);
			int x2[] = { x + 2, x + 5, x + 8 };
			int y2[] = { y + 52, y + 58, y + 52 };
			g.fillPolygon(x2, y2, 3);
		}
	}

    public boolean mouseDown(Event evt, int x, int y)
    {  // handles when the mouse button goes down

		mouseDown = true;

		if (x >= 10 && x <= 20 && y >= 130 && y <= 140)
			buttonDown = 1;
		else if (x >= 10 && x <= 20 && y >= 180 && y <= 190)
			buttonDown = 2;
		else if (x >= 360 && x <= 370 && y >= 130 && y <= 140)
			buttonDown = 3;
		else if (x >= 360 && x <= 370 && y >= 180 && y <= 190)
			buttonDown = 4;

		repaint();
        return true;
    }

    public boolean mouseUp(Event evt, int x, int y)
    {  // handles when the mouse button goes Up


		// Adjust the indices of what elements are visible
		switch (buttonDown) 
		{
			case 1:
				if (topIndex1 != 0)
				{
					topIndex1--;
					botIndex1--;
				}
			break;
			case 2:
				if (botIndex1 != this.SIZE - 1)
				{
					topIndex1++;
					botIndex1++;
				}
			break;
			case 3:
				if (topIndex2 != 0)
				{
					topIndex2--;
					botIndex2--;
				}
			break;
			case 4:
				if (botIndex2 != this.SIZE - 1)
				{
					topIndex2++;
					botIndex2++;
				}
			break;
		}

		buttonDown = 0;
        mouseDown = false;
		repaint();
        return true;
    }

    public boolean mouseDrag(Event evt, int x, int y)
    {  // handles when the mouse is being dragged

		if (x >= 10 && x <= 20 && y >= 130 && y <= 140)
			buttonDown = 1;
		else if (x >= 10 && x <= 20 && y >= 180 && y <= 190)
			buttonDown = 2;
		else if (x >= 360 && x <= 370 && y >= 130 && y <= 140)
			buttonDown = 3;
		else if (x >= 360 && x <= 370 && y >= 180 && y <= 190)
			buttonDown = 4;
		else 
			buttonDown = 0;

        repaint(); 
        return true;
    }

	public void begin(int algorithm, int objectType)
	{

		// Initialize some variables
		topIndex1 = topIndex2 = 0;
		botIndex1 = botIndex2 = 5;

		if (objectType % 2 == 0)
		{ // Create objects
			Object objectArray[] = new Object[this.SIZE];
			objectArray = createObject(objectType);

			preSortObject = new SortableObject(objectArray);
			sObject = new SortableObject(objectArray);

			sAlgorithm = makeAlgorithm(sObject, algorithm);
		}
		else 
			handlePrimitives(algorithm, objectType);

		stopped = false;
		startSort = true;
		startSort();
	}

	public void end()
	{
		stopped = true;
		repaint();
	}

	private SortAlgorithm makeAlgorithm(SortableObject anObject,
		int algorithm)
	{ // Create a new sort algorithm

		SortAlgorithm newAlgorithm;

		if (algorithm == 0)
			newAlgorithm = new MergeSort(anObject, this);
		else if (algorithm == 1)
			newAlgorithm = new QuickSort(anObject, this);
		else 
			newAlgorithm = new RadixSort(anObject, this);

		return newAlgorithm;
	}
	
	private void handlePrimitives(int algorithm, int objectType)
	{ // Create array of primitives, then turn it into a 
	  // sortable object and then create the sort algorithm.

		switch (objectType)
		{
		case 1: // char
			char charArray[];
			charArray = createCharArray();
			preSortObject = new SortableObject(charArray);
			sObject = new SortableObject(charArray);
			break;
		case 3: // double
			double doubleArray[];
			doubleArray = createDoubleArray();
			preSortObject = new SortableObject(doubleArray);
			sObject = new SortableObject(doubleArray);
			break;
		case 5: // float
			float floatArray[];
			floatArray = createFloatArray();
			preSortObject = new SortableObject(floatArray);
			sObject = new SortableObject(floatArray);
			break;
		case 7: // integer
			int intArray[];
			intArray = createIntArray();
			preSortObject = new SortableObject(intArray);
			sObject = new SortableObject(intArray);
			break;
		case 9: // long
			long longArray[];
			longArray = createLongArray();
			preSortObject = new SortableObject(longArray);
			sObject = new SortableObject(longArray);
			break;
		}

		sAlgorithm = makeAlgorithm(sObject, algorithm);

	}

	private Object [] createObject(int objectType)
	{
		Object newObjectArray[] = new Object[this.SIZE];
		
		switch (objectType)
		{
		case 0: // character
			newObjectArray = createCharObject();
			break;
		case 2: // double
			newObjectArray = createDoubleObject();
			break;
		case 4: // float
			newObjectArray = createFloatObject();
			break;
		case 6: // integer
			newObjectArray = createIntegerObject();
			break;
		case 8: // long 
			newObjectArray = createLongObject();
			break;
		case 10: // string
			newObjectArray = createStringObject();
			break;
		}
		return newObjectArray;
	}

	private Object [] createCharObject()
	{ // Create an object array of Chars
	
		Object charArray[] = new Object[this.SIZE];
		char newChar;
		
		for (int i = 0; i < this.SIZE; i++)
		{
			newChar = (char)('a' + (Math.abs(random.nextInt()) % 26));
			charArray[i] = new Character(newChar);
		}

		return charArray;
	}

	private Object [] createStringObject()
	{ // Create an object array of Strings
	
		Object stringArray[] = new Object[this.SIZE];
		char newChar;
		char newString[] = new char[5];

		for (int i = 0; i < this.SIZE; i++)
		{
			for (int j = 0; j < this.STRINGSIZE; j++)
			{
				newChar = (char)('a' + (Math.abs(random.nextInt()) % 26));
				newString[j] = newChar;
			}
			stringArray[i] = new String(newString);
		}


		return stringArray;
	
	}
	
	private Object [] createIntegerObject()
	{ // Create an object array of Integers
	
		Object anArray[] = new Object[this.SIZE];
		
		for (int i = 0; i < this.SIZE; i++)
		{
			anArray[i] = new Integer(random.nextInt() % 1000);
		}

		return anArray;
	}

	private Object [] createDoubleObject()
	{ // Create an object array of Doubles
	
		Object anArray[] = new Object[this.SIZE];
		
		for (int i = 0; i < this.SIZE; i++)
		{
			anArray[i] = new Double(random.nextDouble());
		}

		return anArray;
	}

	private Object [] createFloatObject()
	{ // Create an object array of Floats
	
		Object anArray[] = new Object[this.SIZE];
		
		for (int i = 0; i < this.SIZE; i++)
		{
			anArray[i] = new Float(random.nextFloat());
		}

		return anArray;
	}

	private Object [] createLongObject()
	{ // Create an object array of longs
	
		Object anArray[] = new Object[this.SIZE];
		
		for (int i = 0; i < this.SIZE; i++)
		{
			anArray[i] = new Long(random.nextLong());
		}

		return anArray;
	}

	private char [] createCharArray()
	{ // Create an array of Chars
	
		char charArray[] = new char[this.SIZE];;
		char newChar;
		
		for (int i = 0; i < this.SIZE; i++)
		{
			newChar = (char)('a' + (Math.abs(random.nextInt()) % 26));
			charArray[i] = newChar;
		}

		return charArray;
	}

	private int [] createIntArray()
	{ // Create an array of Integers
	
		int anArray[] = new int[this.SIZE];
		
		for (int i = 0; i < this.SIZE; i++)
		{
			anArray[i] = random.nextInt() % 1000;
		}

		return anArray;
	}

	private double [] createDoubleArray()
	{ // Create an array of Doubles
	
		double anArray[] = new double[this.SIZE];
		
		for (int i = 0; i < this.SIZE; i++)
		{
			anArray[i] = random.nextDouble();
		}

		return anArray;
	}

	private float [] createFloatArray()
	{ // Create an array of Floats
	
		float anArray[] = new float[this.SIZE];
		
		for (int i = 0; i < this.SIZE; i++)
		{
			anArray[i] = random.nextFloat();
		}

		return anArray;
	}

	private long [] createLongArray()
	{ // Create an object array of Longs
	
		long anArray[] = new long[this.SIZE];
		
		for (int i = 0; i < this.SIZE; i++)
		{
			anArray[i] = random.nextLong();
		}

		return anArray;
	}

}
//---END UI CODE---------
