import java.util.Comparator;

/**
 * Class for performing "instrumented" selection sort.
 * (See source code at <a href="../source/SelectionSort.java">
 *   SelectionSort.java</a>.)
 */
public class SelectionSort extends Sorter {

    // ---- methods ----

    /**
     * Sorts array.
     * @param a array to sort (in compareTo orter)
     * @return counters showing number of comparisons and number of swaps
     */
    public static SortResult sort(Comparable[] a) {
        return sort(a, null);
    }

    /**
     * Sorts array.
     * @param a array to sort 
     * @param c comparator, or null to use array's compareTo method (in which 
     *   case its elements should be Comparable)
     * @return counters showing number of comparisons and number of swaps
     */
    public static SortResult sort(Object[] a, Comparator c) {
        Counters counters = new Counters();
        for (int elemToChange = a.length-1; elemToChange > 0; --elemToChange) 
            counters.swap(a, elemToChange, 
                    findMaxIndex(a, elemToChange, c, counters));
        return new SortResult(counters.numCompares(), counters.numSwaps());
    }

    /**
     * Finds maximum index in (part of) array.
     * @param a array
     * @param lastIndex last index to check
     * @param c comparator, or null to use array's compareTo method (in which
     *   case its elements should be Comparable)
     * @param counters object with counters to update
     * @return index of largest element in a[0 .. lastIndex]
     */
    private static int findMaxIndex(Object[] a, int lastIndex, Comparator c,
            Counters counters) {
        int indexOfMax = 0;
        for (int i = 1; i <= lastIndex; ++i) {
            if (counters.compare(a[i], a[indexOfMax], c) > 0)
                indexOfMax = i;
        }
        return indexOfMax;
    }

    /**
     * Tests sort:
     *   Performs sort, tests result, and prints values of counters, preceded 
     *   by header message.
     * @param testName "test name" for header message
     * @param a array to sort
     * @param c comparator, or null to use compareTo
     */
    public static void testSort(String testName, Object[] a, Comparator c) {
        SortResult r = sort(a, c);
        printSortResult(testName, "selection sort", a, c, r);
    }

    // ---- main method ----

    /**
     * Performs simple tests.
     * @param args command-line arguments -- data to sort, or none to use
     *   default data
     */
    public static void main(String[] args) {

        if (args.length < 1) {
        // Make some test data.
            args = new String[] {
                "hello", "bye", "HELLO", "abcd", "100", "!@#$" 
            };
        }

        // Make a comparator object to compare two Strings, ignoring case.
        Comparator ciCompare = new Comparator() {
            public int compare(Object o1, Object o2) {
                if ((o1 instanceof String) && (o2 instanceof String))
                    return ((String) o1).toUpperCase().compareTo(
                            ((String) o2).toUpperCase());
                else throw new ClassCastException();
            }
        };

        // Make "test names".
        String rName = "strings (regular)";
        String ciName = "strings (case-insensitive)";

        // Print test data.
        printArray("Input data:", args);

        // Try out sort.
        String[] copyOfData = (String[]) args.clone();
        System.out.println("");
        testSort(rName, copyOfData, null);
        printArray("Result:", copyOfData);
        testSort(ciName, copyOfData, ciCompare);
        printArray("Result:", copyOfData);
    }
}
