package org.jcon.util;
import java.util.Locale;
import java.text.Collator;
import java.text.CollationKey;
import org.jcon.ui.MessageBox;

/**
 * Sorts an array using a Comparer. All static methods.
 * Initially only a few sort types are supported. The
 * example has many.
 * From Nutshell Examples page 36 with minor modifications.
 *
 * @author Jack Harich
 */
public class ArraySorter {

//---------- Public Fields -------------------------------
public static final ASCIIComparer asciiComparer
    = new ASCIIComparer();

//---------- Initialization ------------------------------
/**
 * Performs a simple unit test by sorting command arguments.
 */
public static void main(String args[]) {
    if (args.length == 0) {
        print(" - Syntax: command args will be sorted");
    } else {

		// Test the ASCII sort
        ArraySorter.sortASCII(args);
        for (int i = 0; i < args.length; i++) {
            print(" - " + args[i]);
        }

		// Test the Collated sort using no Locale
        ArraySorter.sortCollated(args, null);
        for (int i = 0; i < args.length; i++) {
            print(" - Collated - " + args[i]);
        }

		// Test the Collated sort with a predefined
		// array containing cedilla.  The Cedilla should
		// appear after "c" in the sorted array
		// NOTE: The Cedilla doesn't print correctly to stdout because
		// the printstream does not print unicode characters, but
		// the characters appear in the correct order.
		// the MessageBox will show the correct characters
		/*
		String[] myarray = {    "\u00e7",  // lowercase cedilla
								"\u00c7",  // capital cedilla
								"\u0061",  // a
								"\u0063",  // c
								"\u007a"}; // z

        ArraySorter.sortCollated(myarray, Locale.FRANCE);
		String collatedText = new String("Sorted Text: ");

        for (int i = 0; i < myarray.length; i++) {
			collatedText += myarray[i] + " ";
            print(" - Collated - " + myarray[i]);
        }
		MessageBox mb = new MessageBox();
		mb.askOkay(collatedText);
		*/

		
    }

}
//---------- Public Methods ------------------------------
/**
 * Sorts an ASCII String array in ascending order.
 * Case insensitive, which is usually desired.
 */
public static void sortASCII(String[] a) {
    // Build new lowercase array
    String[] b = new String[a.length];
    for (int i = 0; i < a.length; i++) {
        b[i] = a[i].toLowerCase();
    }
    // Sort b[] and use to sort a[]
    sort(b, a, 0, a.length - 1, true, asciiComparer);
}
public static void sortCollated(String[] a, Locale locale) {
    // Sort b[] and use to sort a[]
    sortStringsByCollation(a, 0, a.length - 1, true, true, locale);
}
/**
 * Sorts the Object array in ascending order, using the
 * ASCII String array. Case insensitive.
 */
public static void sortObjectsByASCII(String[] strings, Object[] objects) {
    // Build new lowercase array
    String[] b = new String[strings.length];
    for (int i = 0; i < strings.length; i++) {
        b[i] = strings[i].toLowerCase();
    }
    // Sort b[] and use to sort objects[]
    sort(b, objects, 0, strings.length - 1, true, asciiComparer);
}
/**
 * Sorts the array in ascending order using the Comparer.
 */
public static void sort(Object[] a, Comparer comparer) {
    sort(a, null, 0, a.length - 1, true, comparer);
}
/**
 * Sorts both arrays. Array b is optional.
 * from and to determine the portion of the arrays
 * sorted, which is usually all. ascending is true
 * for an ascending sort, false for decending. The
 * comparer is used for object comparisons. Quicksort.
 */
public static void sort(Object[] a, Object[] b, int from,
        int to, boolean ascending, Comparer comparer) {

    // No sort if nothing to sort
    if (a == null || a.length < 2) return;

    // Quicksort
    int i = from, j = to;
    Object center = a[ (from + to) / 2 ];
    do {
        if (ascending) {
            while( (i < to) && (comparer.compare(  center, a[i]) > 0) ) i++;
            while( (j > from) && (comparer.compare(center, a[j]) < 0) ) j--;
        } else {
            // Decending sort
            while( (i < to) && (comparer.compare(  center, a[i]) < 0) ) i++;
            while( (j > from) && (comparer.compare(center, a[j]) > 0) ) j--;
        }
        if (i < j) {
            // Swap elements
            Object temp = a[i];
            a[i] = a[j];
            a[j] = temp;
            // Swap in b array also
            if (b != null) {
                temp = b[i];
                b[i] = b[j];
                b[j] = temp;
            }
        }
        if (i <= j) { i++; j--; }
    } while(i <= j);
    // Recursively sort the rest
    if (from < j) sort(a, b, from, j, ascending, comparer);
    if (i < to) sort(a, b, i, to, ascending, comparer);
}

/**
* Sort an array of strings based on a locale, if no locale is specified, the
* default is used.
*/
public static void sortStringsByCollation(String[] strings, 
			int fromIndex, int toIndex, boolean asc, 
			boolean ignoreCase, Locale locale) {

		/*print("Entering sortStringsByCollation");
		print("\nargs:      " 
			  + "\nstrings    " + strings[0].toString() 
			  + "\nstrings    " + strings[1].toString() 
		      + "\nfromIndex  " + fromIndex 
			  + "\ntoIndex    " + toIndex 
			  + "\nasc        " + asc 
			  + "\nignoreCase " + ignoreCase 
			  + "\nlocale     " + locale );
		*/


		//------------------------------------------------------
		// Don't sort if less than 2 strings.
		//------------------------------------------------------
		if ((strings == null) || (strings.length < 2)) { return; }

		//------------------------------------------------------
		// Get the locale specific Collator
		//------------------------------------------------------
		Collator collator;
		if (locale == null) { 
				// use the default if no locale is specified
				collator = Collator.getInstance();
		} else {
				collator = Collator.getInstance(locale);
		}

		if (ignoreCase == true) {
				collator.setStrength(Collator.SECONDARY);
		}

		//------------------------------------------------------
		// Create an array of collation key objects.
		// this is used because sorting a list requires testing
		// an element multiple times, and CollationKeys are
		// faster.
		//------------------------------------------------------
		CollationKey[] ckeys = new CollationKey[strings.length];

		for (int i = 0; i < strings.length; i++) {
				ckeys[i] = collator.getCollationKey(strings[i]);
		}
		//------------------------------------------------------
		// Create the comparer
		//------------------------------------------------------
		CollKeyComparer comp = new CollKeyComparer();

		//------------------------------------------------------
		// Sort the array of keys and strings
		//------------------------------------------------------
		sort(ckeys, strings, fromIndex, toIndex, asc, comp);
		
}

//---------- Private Methods -----------------------------
//--- Std
private static void print(String text) {
    System.out.println("ArraySorter" + text);
}
//========== Inner Classes ===============================
/**
 * The interface implementation should compare the two
 * objects and return an int using these rules:
 * if (a > b)  return > 0;
 * if (a == b) return 0;
 * if (a < b)  return < 0;
 *
 *      For example to sort a String array:
 * ClassName implements ArraySorter.Comparer
 *
 * public int compare(Object a, Object b) {
 *     return (the proper int);
 * }
 */
public static interface Comparer {
    public int compare(Object a, Object b);
} // End inner class

/**
 * Compares assuming the String is only ASCII. Case insensitive.
 */
public static class ASCIIComparer implements Comparer {
    public int compare(Object a, Object b) {
        return ((String)a).compareTo((String)b);
    }
} // End inner class

public static class CollKeyComparer implements Comparer { 
		public int compare(Object a, Object b) {
				return ((CollationKey)a).compareTo((CollationKey)b);
		}
}



} // End outer class
