/**
 * Licensed under the MICE s.r.l.
 * End User License Agreement
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.micegroup.it/voila/license.html
 */
package it.mice.voila.runtime.entity;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Iterator;

import org.apache.commons.beanutils.PropertyUtils;

/**
 * This utility class is used to sort the list that the table is viewing
 * based on arbitrary properties of objects contained in that list.  The
 * only assumption made is that the object returned by the property is
 * either a native type, or implements the Comparable interface.
 *
 * If the property does not implement Comparable, then this little sorter
 * object will just assume that all objects are the same, and quietly do
 * nothing (so you should check for Comparable objecttypes elsewhere as
 * this object won't complain).
 **/

public class BeanComparator extends Object implements Comparator<Object> {
	private ArrayList<String> propertyList;

	/**
	 * BeanSorter is a decorator of sorts, you need to initialize it with
	 * the property name of the object that is to be sorted (getXXX method
	 * name).  This property should return a Comparable object.
	 */

	public BeanComparator(String[] propertyList) {
		this.propertyList = new ArrayList<String>();
		for (int i=0; i<propertyList.length; i++) 
			this.propertyList.add(propertyList[i]);
	}

	public BeanComparator(ArrayList<String> propertyList) {
		this.propertyList = propertyList;
	}

	public BeanComparator(String property) {
		this.propertyList = new ArrayList<String>(1);
		this.propertyList.add(property);
	}

	/**
	 * Compares two objects by first fetching a property from each object
	 * and then comparing that value.  If there are any errors produced while
	 * trying to compare these objects then a RunTimeException will be
	 * thrown as any error found here will most likely be a programming
	 * error that needs to be quickly addressed (like trying to compare
	 * objects that are not comparable, or trying to read a property from a
	 * bean that is invalid, etc...)
	 *
	 * @throws RuntimeException if there are any problems making a comparison
	 *    of the object properties.
	 */

	public int compare(Object o1, Object o2) throws RuntimeException {
		if (propertyList == null) {
			throw new NullPointerException("Null property list provided which prevents BeanSorter sort");
		}
		if (propertyList.size() == 0) {
			throw new RuntimeException("Zero lenght property list provided which prevents BeanSorter sort");
		}

		return doComparation(o1, o2);
	}

	private int doComparation(Object o1, Object o2) {
		// If they have supplied a decorator, then make sure and use it for
		// the sorting as well... TODO - Major hack....

		Iterator<String> i = propertyList.iterator();
		int result = 0;
		while (i.hasNext() && result == 0) {
			String property = i.next();
			try {
				Object p1 = null;
				Object p2 = null;
				boolean sortAscending = true;
				char lastChar = property.charAt(property.length() - 1);
				if (lastChar == '>') {
					sortAscending = false;
					property = property.substring(0, property.length() - 1);
				}
				if (lastChar == '<') {
					sortAscending = true;
					property = property.substring(0, property.length() - 1);
				}
				p1 = PropertyUtils.getProperty(o1, property);
				p2 = PropertyUtils.getProperty(o2, property);

				if (p1 != null || p2 != null) {
					result = doCompCalculation(result, property, p1, p2,
							sortAscending);
				} else {
					return 0;
				}
			} catch (IllegalAccessException e) {
				throw new RuntimeException(
					"IllegalAccessException thrown while "
						+ "trying to fetch property \""
						+ property
						+ "\" during sort");
			} catch (InvocationTargetException e) {
				throw new RuntimeException(
					"InvocationTargetException thrown while "
						+ "trying to fetch property \""
						+ property
						+ "\" during sort");
			} catch (NoSuchMethodException e) {
				throw new RuntimeException(
					"NoSuchMethodException thrown while "
						+ "trying to fetch property \""
						+ property
						+ "\" during sort");
			}
		}
		return result;
	}

	@SuppressWarnings("unchecked")
	private int doCompCalculation(int result, String property, Object p1, Object p2, boolean sortAscending) {
		if (p1 == null) {
			if (sortAscending) {
				result = -1;
			} else {
				result = 1;
			}
		}
		if (p2 == null) {
			if (sortAscending) {
				result = 1;
			} else {
				result = -1;
			}
		}
			
		if (result == 0) {
			if (p1 instanceof Comparable && p2 instanceof Comparable) {
				Comparable<Object> c1 = (Comparable<Object>) p1;
				Comparable<Object> c2 = (Comparable<Object>) p2;
				if (sortAscending)
					result = c1.compareTo(c2);
				else
					result = c2.compareTo(c1);
			} else {
				throw new RuntimeException(
					"Object returned by property \""
						+ property
						+ "\" is not a Comparable object");
			}
		}
		return result;
	}

}
