/*
 * @(#)SortItem.java	1.17f 95/04/10 James Gosling
			1.18  96/4/24  Jim Hagen : use setBackground
 *
 * Copyright (c) 1994-1996 Sun Microsystems, Inc. All Rights Reserved.
 *
 * Permission to use, copy, modify, and distribute this software
 * and its documentation for NON-COMMERCIAL or COMMERCIAL purposes and
 * without fee is hereby granted. 
 * Please refer to the file http://java.sun.com/copy_trademarks.html
 * for further important copyright and trademark information and to
 * http://java.sun.com/licensing.html for further important licensing
 * information for the Java (tm) Technology.
 * 
 * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF
 * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
 * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
 * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR
 * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
 * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
 * 
 * THIS SOFTWARE IS NOT DESIGNED OR INTENDED FOR USE OR RESALE AS ON-LINE
 * CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING FAIL-SAFE
 * PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES, AIRCRAFT
 * NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT LIFE
 * SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
 * SOFTWARE COULD LEAD DIRECTLY TO DEATH, PERSONAL INJURY, OR SEVERE
 * PHYSICAL OR ENVIRONMENTAL DAMAGE ("HIGH RISK ACTIVITIES").  SUN
 * SPECIFICALLY DISCLAIMS ANY EXPRESS OR IMPLIED WARRANTY OF FITNESS FOR
 * HIGH RISK ACTIVITIES.
 */

import java.awt.*;
import java.io.InputStream;
import java.util.Hashtable;
import java.net.*;

/**
 * A simple applet class to demonstrate a sort algorithm.
 * You can specify a sorting algorithm using the "alg"
 * attribyte. When you click on the applet, a thread is
 * forked which animates the sorting algorithm.
 *
 * @author James Gosling
 * @version 	1.17f, 10 Apr 1995
 */
public class SortItem extends java.applet.Applet implements Runnable {
    /**
     * The thread that is sorting (or null).
     */
    private Thread kicker;

    /**
     * The array that is being sorted.
     */
    int arr[];
    int oldarr[];

    /**
     * The high water mark.
     */
    int h1 = -1;
    int oldh1 = h1;

    /**
     * The low water mark.
     */
    int h2 = -1;
    int oldh2 = h2;

    /**
     * The name of the algorithm.
     */
    String algName;

    /**
     * The sorting algorithm (or null).
     */
    SortAlgorithm algorithm;

    int array_size = 64;
    int rectheight = 3;
    int rectspace  = 3;
    int applet_width = 300;
    int applet_height = 475;
    protected int pause_milli_secs = 100;
    int min_millis = 20;
    int max_millis = 1000;
    int graphics_y_start;
    Button title_button;
    Button faster;
    Button much_faster;
    Button slower;
    Button much_slower;
    TextField speed;
    Panel speed_panel;

    /**
     * Fill the array with random numbers from 0..n-1.
     */
    void scramble() {
	//int a[] = new int[size().height / 2];
	int a[] = new int[array_size];
	//double f = size().width / (double) a.length;
	double f = 2.0;
	int i;
	for (i = a.length; --i >= 0;) {
	    a[i] = (int)(i * f);
	}
	for (i = a.length; --i >= 0;) {
	    int j = (int)(i * Math.random());
	    int t = a[i];
	    a[i] = a[j];
	    a[j] = t;
	}
	arr = a;

	oldarr = new int [array_size];
	for (i = 0; i < array_size; i++) {
	    // oldarr[i] = arr[i];
	    oldarr[i] = -1;
	}
    }

    /**
     * Pause a while.
     * @see SortAlgorithm
     */
    void pause() {
	pause(-1, -1);
    }

    /**
     * Pause a while, and draw the high water mark.
     * @see SortAlgorithm
     */
    void pause(int H1) {
	pause(H1, -1);
    }

    /**
     * Pause a while, and draw the low&high water marks.
     * @see SortAlgorithm
     */
    void pause(int H1, int H2) {
	h1 = H1;
	h2 = H2;
	if (kicker != null) {
	    repaint();
	}
	try {Thread.sleep(pause_milli_secs);} catch (InterruptedException e){}
    }

    /**
     * Initialize the applet.
     */
    public void init() {
	String at = getParameter("alg");
	if (at == null) {
	    at = "NoSort";
	}

	title_button = new Button ("Start " + at);
	this.add (title_button);

	speed_panel = new Panel();
	faster = new Button ("F");
	much_faster = new Button ("FF");
	slower = new Button ("S");
	much_slower = new Button ("SS");
	speed = new TextField("Speed", 7);
	speed.setEditable(false);

	speed_panel.setLayout(new FlowLayout (FlowLayout.CENTER, 15, 5));
	speed_panel.add (much_slower);
	speed_panel.add (slower);
	speed_panel.add (speed);
	speed_panel.add (faster);
	speed_panel.add (much_faster);

	add (speed_panel);

	graphics_y_start = 100; 

	algName = at + "Algorithm";
	scramble();

	resize(applet_width, applet_height);
    }

    public boolean action (Event event, Object arg) {
	if (event.target == title_button) {
	    startSort();
	} else if (event.target == much_slower) {
	    if (pause_milli_secs <= max_millis - 100) {
	        set_pause (pause_milli_secs + 100);
	    }
	} else if (event.target == slower) {
	    if (pause_milli_secs <= max_millis - 20) {
	        set_pause (pause_milli_secs + 20);
	    }
	} else if (event.target == faster) {
	    if (pause_milli_secs >= min_millis + 20) {
	        set_pause (pause_milli_secs - 20);
	    }
	} else if (event.target == much_faster) {
	    if (pause_milli_secs >= min_millis + 100) {
	        set_pause (pause_milli_secs - 100);
	    }
	} else {
            return super.action (event, arg);
	}
	return true;
    }

    protected void set_pause (int millis) {
        if ((millis <= max_millis) && (millis >= min_millis)) {
	    pause_milli_secs = millis;
	    speed.setText ("ms " + pause_milli_secs);
	}

	if (pause_milli_secs < min_millis + 100) {
	    much_faster.disable();
	} else {
	    much_faster.enable();
	}

	if (pause_milli_secs < min_millis + 20) {
	    faster.disable();
	} else {
	    faster.enable();
	}

	if (pause_milli_secs > max_millis - 100) {
	    much_slower.disable();
	} else {
	    much_slower.enable();
	}

	if (pause_milli_secs > max_millis - 20) {
	    slower.disable();
	} else {
	    slower.enable();
	}
    }

    /**
     * Paint the array of numbers as a list
     * of horizontal lines of varying lenghts.
     */
    public void paint(Graphics g) {
	int a[] = arr;
	int y;

	// Erase old lines
	// Try to be smart about this - only erase lines that have
	// changed since the last redraw.  Also erase lines where
	// the high and low watermarks were
	g.setColor(getBackground());
	y = graphics_y_start;
	for (int i = 0; i < arr.length; i++, y += rectheight + rectspace) {
	    if (oldarr[i] != arr[i]) {
		//System.out.println ("A" + oldarr[i] + arr[i]);
	        g.fillRect (0, y, size().width, rectheight);
	    }
	    if (i == oldh1) {
		//System.out.println ("B");
	        g.fillRect (0, y, size().width, rectheight);
	    }
	    if (i == oldh2) {
		//System.out.println ("C");
	        g.fillRect (0, y, size().width, rectheight);
	    }
	}

	// Draw new lines
	g.setColor(Color.black);
	y = graphics_y_start;
	for (int i = 0; i < arr.length; i++, y += rectheight + rectspace) {
	    g.fillRect (0, y, arr[i] * 2, rectheight);
	}

	y = graphics_y_start;
	if (h1 >= 0) {
	    g.setColor(Color.red);
	    y += (h1) * (rectheight + rectspace) + (rectheight / 2);
	    g.drawLine(0, y, size().width, y);
	}
	y = graphics_y_start;
	if (h2 >= 0) {
	    g.setColor(Color.blue);
	    y += (h2) * (rectheight + rectspace) + (rectheight / 2);
	    g.drawLine(0, y, size().width, y);
	}
	oldh1 = h1;
	oldh2 = h2;
    }

    /**
     * Update without erasing the background.
     */
    public void update(Graphics g) {
	paint(g);
	//oldh1 = h1;
	//oldh2 = h2;
	for (int i = 0; i < arr.length; i++) {
	    oldarr[i] = arr[i];
	}
    }

    /**
     * Run the sorting algorithm. This method is
     * called by class Thread once the sorting algorithm
     * is started.
     * @see java.lang.Thread#run
     * @see SortItem#mouseUp
     */
    public void run() {
	try {
	    if (algorithm == null) {
		algorithm = (SortAlgorithm)Class.forName(algName).newInstance();
		algorithm.setParent(this);
	    }
	    algorithm.init();
	    algorithm.sort(arr);
	} catch(Exception e) {
	}
    }

    /**
     * Stop the applet. Kill any sorting algorithm that
     * is still sorting.
     */
    public synchronized void stop() {
	if (kicker != null) {
            try {
		kicker.stop();
            } catch (IllegalThreadStateException e) {
                // ignore this exception
            }
	    kicker = null;
	}
	if (algorithm != null){
            try {
		algorithm.stop();
            } catch (IllegalThreadStateException e) {
                // ignore this exception
            }
	}
    }


    /**
     * For a Thread to actually do the sorting. This routine makes
     * sure we do not simultaneously start several sorts if the user
     * repeatedly clicks on the sort item.  It needs to be
     * synchronoized with the stop() method because they both
     * manipulate the common kicker variable.
     */
    private synchronized void startSort() {
	if (kicker == null || !kicker.isAlive()) {
	    scramble();
	    repaint();
	    kicker = new Thread(this);
	    kicker.start();
	}
    }


}
