package org.mandarax.util.comparators;

/*
 * Copyright (C) 1999-2004 <a href="mailto:mandarax@jbdietrich.com">Jens Dietrich</a>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
 
import java.util.Arrays;
import java.util.Comparator;

import org.mandarax.kernel.ClauseSet;
import org.mandarax.kernel.Fact;
import org.mandarax.kernel.Rule;
import org.mandarax.sql.SQLClauseSet;

/**
 * Default clause set comparator class that can be used in order to
 * arrange knowledge in an extended knowledge base automatically.
 * The following policy is employed:
 * <ol>
 * <li>SQL clause sets have the highest priority.
 * <li>Facts have the second highest priority. Among facts, the order is managed by 
 * a chain of fact comparators.
 * <li>Rules have lower priority than facts. Among rules, the order is managed by 
 * a chain of rule comparators.
 * <li>All other types of clause sets have a lower priority (this rules
 * can be critical.
 * </ol>
 * @author <A HREF="mailto:mandarax@jbdietrich.com">Jens Dietrich</A>
 * @version 3.3.1 <9 August 2004>
 * @since 2.2
 */
public class DefaultClauseSetComparator extends AbstractClauseSetComparator {
 	public final String RULES = "rules";
 	public final String FACTS = "facts";
 	public final String SQL_CLAUSE_SETS = "sql clause sets";
 	public final String OTHERS = "others";

 	// properties
 	private String[] topLevelOrder = {FACTS,SQL_CLAUSE_SETS,RULES,OTHERS};
 	private Comparator[] comparators4Facts = {new PreferFactsWithLessVariables()};
 	private Comparator[] comparators4Rules = {
	 	new PreferRulesWithMorePrerequisites(),
	 	new PreferRulesWithLessNegatedPrerequisites(),
	 	new PreferRulesWithLessVariablesInPrerequisites()	 	
	};
 	private Comparator[] comparators4SQLClauseSets = {};
 	private Comparator[] comparators4Others = {};
/**
 * Constructor.
 */
public DefaultClauseSetComparator() {
	super();
}
/**
 * Compare two objects.
 * @return an integer
 * @param obj1 the first object
 * @param obj2 the second object
 */
public int compare(Object obj1, Object obj2) {
	if (obj1 instanceof ClauseSet && obj2 instanceof ClauseSet) return compareClauseSets((ClauseSet)obj1,(ClauseSet)obj2);
	else throw new IllegalArgumentException("The clause set comparator can only compare clause sets");
}
/**
 * Compare two clause sets.
 * @return an integer
 * @param cs1 the first clause set
 * @param cs2 the second clause set
 */
private int compareClauseSets(ClauseSet cs1, ClauseSet cs2) {
	int diff = getMajorSortKeyValue(cs2)-getMajorSortKeyValue(cs1);
	if (diff==0) {
		// use special comparators
		if (cs1 instanceof Rule && cs2 instanceof Rule) {
			return compareInChain(cs1,cs2,comparators4Rules);	
		}
		else if (cs1 instanceof Fact && cs2 instanceof Fact) {
			return compareInChain(cs1,cs2,comparators4Facts);	
		}
		else if (cs1 instanceof SQLClauseSet && cs2 instanceof SQLClauseSet) {
			return compareInChain(cs1,cs2,comparators4SQLClauseSets);	
		}
		else {
			return compareInChain(cs1,cs2,comparators4Others);	
		}
	}
	return diff;
}
/**
 * Use a chain of comparators to compare two objects
 * @param obj1 object 1
 * @param obj2 object 2
 * @param comparators the comparator chain
 * @return the result of comparing the objects
 */
private int compareInChain(Object obj1, Object obj2, Comparator[] comparators) {
	int diff = 0;
	for (int i=0;i<comparators.length;i++) {
		diff = comparators[i].compare(obj1,obj2);
		if (diff!=0) return diff;	
	}
	return diff;
}
/**
 * Get the value of the major sort key.
 * @return an integer
 * @param cs a clause set
 */
private int getMajorSortKeyValue(ClauseSet cs) {
	String type = OTHERS;
	if (cs instanceof Rule) type = RULES;
	else if (cs instanceof Fact) type = FACTS;
	else if (cs instanceof SQLClauseSet) type = SQL_CLAUSE_SETS;

	for (int i=0;i<topLevelOrder.length;i++) {
		if (topLevelOrder[i].equals(type)) return topLevelOrder.length-i;	
	}
	return 0;
}
/**
 * Get a descriptive name.
 * @return a string
 */
public String getName() {
	return "default comparator";	
}
	/**
	 * Returns the comparators4Facts.
	 * @return Comparator[]
	 */
	public Comparator[] getComparators4Facts() {
		return comparators4Facts;
	}

	/**
	 * Returns the comparators4Others.
	 * @return Comparator[]
	 */
	public Comparator[] getComparators4Others() {
		return comparators4Others;
	}

	/**
	 * Returns the comparators4Rules.
	 * @return Comparator[]
	 */
	public Comparator[] getComparators4Rules() {
		return comparators4Rules;
	}

	/**
	 * Returns the comparators4SQLClauseSets.
	 * @return Comparator[]
	 */
	public Comparator[] getComparators4SQLClauseSets() {
		return comparators4SQLClauseSets;
	}

	/**
	 * Returns the topLevelOrder.
	 * @return String[]
	 */
	public String[] getTopLevelOrder() {
		return topLevelOrder;
	}

	/**
	 * Sets the comparators4Facts.
	 * @param comparators4Facts The comparators4Facts to set
	 */
	public void setComparators4Facts(Comparator[] comparators4Facts) {
		this.comparators4Facts = comparators4Facts;
	}

	/**
	 * Sets the comparators4Others.
	 * @param comparators4Others The comparators4Others to set
	 */
	public void setComparators4Others(Comparator[] comparators4Others) {
		this.comparators4Others = comparators4Others;
	}

	/**
	 * Sets the comparators4Rules.
	 * @param comparators4Rules The comparators4Rules to set
	 */
	public void setComparators4Rules(Comparator[] comparators4Rules) {
		this.comparators4Rules = comparators4Rules;
	}

	/**
	 * Sets the comparators4SQLClauseSets.
	 * @param comparators4SQLClauseSets The comparators4SQLClauseSets to set
	 */
	public void setComparators4SQLClauseSets(Comparator[] comparators4SQLClauseSets) {
		this.comparators4SQLClauseSets = comparators4SQLClauseSets;
	}

	/**
	 * Sets the topLevelOrder.
	 * @param topLevelOrder The topLevelOrder to set
	 */
	public void setTopLevelOrder(String[] topLevelOrder) {
		this.topLevelOrder = topLevelOrder;
	}
	/**
	 * Compares objects.
	 * @param obj an object
	 * @return a boolean
	 */
	public boolean equals(Object obj) {
		if (obj instanceof DefaultClauseSetComparator) {
			DefaultClauseSetComparator comp = (DefaultClauseSetComparator) obj;
			if (!Arrays.equals(topLevelOrder,comp.topLevelOrder)) return false;
			if (!Arrays.equals(comparators4Rules,comp.comparators4Rules)) return false;
			if (!Arrays.equals(comparators4Facts,comp.comparators4Facts)) return false;
			if (!Arrays.equals(comparators4Others,comp.comparators4Others)) return false;
			if (!Arrays.equals(comparators4SQLClauseSets,comp.comparators4SQLClauseSets)) return false;

			return true;
		}
		return false;
	}
	/**
	 * Get the hash code of this object.
	 * @return an integer
	 */
	public int hashCode() {
		return topLevelOrder.hashCode() ^ comparators4Rules.hashCode() ^ comparators4Facts.hashCode() ;
	}

}
