/*******************************************************************************
 * Copyright (c) 2003, 2011 IBM Corporation and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/

package org.eclipse.equinox.console.commands;

/**
 * This class contains utility functions.
 */
public class Util {
	/**
	 * Performs a quicksort of the given objects
	 * by their string representation in ascending order.
	 * <p> 
	 *
	 * @param array	The array of objects to sort
	 */
	public static void sortByString(Object[] array) {
		qSortByString(array, 0, array.length - 1);
	}

	/**
	 * Sorts the array of objects by their string representation
	 * in ascending order.
	 * <p>
	 * This is a version of C.A.R Hoare's Quick Sort algorithm.
	 *
	 * @param array	the	array of objects to sort
	 * @param start	the start index to begin sorting
	 * @param stop		the end index to stop sorting
	 * 
	 * @exception	ArrayIndexOutOfBoundsException when <code>start < 0</code>
	 *				or <code>end >= array.length</code>
	 */
	public static void qSortByString(Object[] array, int start, int stop) {
		if (start >= stop)
			return;

		int left = start; // left index
		int right = stop; // right index
		Object temp; // for swapping

		// arbitrarily establish a partition element as the midpoint of the array
		String mid = String.valueOf(array[(start + stop) / 2]);

		// loop through the array until indices cross
		while (left <= right) {
			// find the first element that is smaller than the partition element from the left
			while ((left < stop) && (String.valueOf(array[left]).compareTo(mid) < 0)) {
				++left;
			}
			// find an element that is smaller than the partition element from the right
			while ((right > start) && (mid.compareTo(String.valueOf(array[right])) < 0)) {
				--right;
			}
			// if the indices have not crossed, swap
			if (left <= right) {
				temp = array[left];
				array[left] = array[right];
				array[right] = temp;
				++left;
				--right;
			}
		}
		// sort the left partition, if the right index has not reached the left side of array
		if (start < right) {
			qSortByString(array, start, right);
		}
		// sort the right partition, if the left index has not reached the right side of array
		if (left < stop) {
			qSortByString(array, left, stop);
		}
	}

	/**
	 * Sorts the specified range in the array in ascending order.
	 *
	 * @param		array	the Object array to be sorted
	 * @param		start	the start index to sort
	 * @param		end		the last + 1 index to sort
	 *
	 * @exception	ClassCastException when an element in the array does not
	 *				implement Comparable or elements cannot be compared to each other
	 * @exception	IllegalArgumentException when <code>start > end</code>
	 * @exception	ArrayIndexOutOfBoundsException when <code>start < 0</code>
	 *				or <code>end > array.size()</code>
	 */
	@SuppressWarnings("unchecked")
	public static void sort(Object[] array, int start, int end) {
		int middle = (start + end) / 2;
		if (start + 1 < middle)
			sort(array, start, middle);
		if (middle + 1 < end)
			sort(array, middle, end);
		if (start + 1 >= end)
			return; // this case can only happen when this method is called by the user
		if (((Comparable<Object>) array[middle - 1]).compareTo(array[middle]) <= 0)
			return;
		if (start + 2 == end) {
			Object temp = array[start];
			array[start] = array[middle];
			array[middle] = temp;
			return;
		}
		int i1 = start, i2 = middle, i3 = 0;
		Object[] merge = new Object[end - start];
		while (i1 < middle && i2 < end) {
			merge[i3++] = ((Comparable<Object>) array[i1]).compareTo(array[i2]) <= 0 ? array[i1++] : array[i2++];
		}
		if (i1 < middle)
			System.arraycopy(array, i1, merge, i3, middle - i1);
		System.arraycopy(merge, 0, array, start, i2 - start);
	}

	/**
	 * Sorts the specified range in the array in descending order.
	 *
	 * @param		array	the Object array to be sorted
	 * @param		start	the start index to sort
	 * @param		end		the last + 1 index to sort
	 *
	 * @exception	ClassCastException when an element in the array does not
	 *				implement Comparable or elements cannot be compared to each other
	 * @exception	IllegalArgumentException when <code>start > end</code>
	 * @exception	ArrayIndexOutOfBoundsException when <code>start < 0</code>
	 *				or <code>end > array.size()</code>
	 */
	public static void dsort(Object[] array, int start, int end) {
		// first sort in ascending order
		sort(array, start, end);
		// then swap the elements in the array
		swap(array);
	}

	/**
	 *  Reverse the elements in the array.
	 *  
	 * @param		array	the Object array to be reversed
	 */
	public static void swap(Object[] array) {
		int start = 0;
		int end = array.length - 1;
		while (start < end) {
			Object temp = array[start];
			array[start++] = array[end];
			array[end--] = temp;
		}
	}

	/**
	 * Returns a string representation of the object
	 * in the given length.
	 * If the string representation of the given object
	 * is longer then it is truncated.
	 * If it is shorter then it is padded with the blanks
	 * to the given total length.
	 * If the given object is a number then the padding
	 * is done on the left, otherwise on the right.
	 *
	 * @param	object	the object to convert
	 * @param	length	the length the output string
	 */
	public static String toString(Object object, int length) {
		boolean onLeft = object instanceof Number;
		return toString(object, length, ' ', onLeft);
	}

	/**
	 * Returns a string representation of the object
	 * in the given length.
	 * If the string representation of the given object
	 * is longer then it is truncated.
	 * If it is shorter then it is padded to the left or right
	 * with the given character to the given total length.
	 *
	 * @param	object	the object to convert
	 * @param	length	the length the output string
	 * @param	pad		the pad character
	 * @param	onLeft	if <code>true</code> pad on the left, otherwise an the right
	 */
	public static String toString(Object object, int length, char pad, boolean onLeft) {
		String input = String.valueOf(object);
		int size = input.length();
		if (size >= length) {
			int start = (onLeft) ? size - length : 0;
			return input.substring(start, length);
		}

		StringBuilder padding = new StringBuilder(length - size);
		for (int i = size; i < length; i++)
			padding.append(pad);

		StringBuilder stringBuffer = new StringBuilder(length);
		if (onLeft)
			stringBuffer.append(padding);
		stringBuffer.append(input);
		if (!onLeft)
			stringBuffer.append(padding);
		return stringBuffer.toString();
	}
}
