package ws.prova.reference;

/*
 * Copyright (C) 1999-2005 <a href="mailto:adrian.paschke@gmx.de">Adrian Paschke</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 org.mandarax.kernel.*;
import org.mandarax.reference.AbstractKnowledgeBase;
import org.mandarax.sql.DefaultTypeMapping;

import ws.prova.sql.SQLExtendedPredicate;
import ws.prova.sql.SQLExtendedClauseSet;
import ws.prova.RList;
import ws.prova.RListUtils;
import ws.prova.DataSource;
import ws.prova.calc.CalcPredicate;
import ws.prova.calc.CalcClauseSet;

import java.util.*;

/**
 * Optimized ContractLog-specific knowledge base managing clause sets
 * in anordered container (like a double linked list). Clauses are indexed
 * by a DAG like data structure. Partial reasoning using the modules' (clause sets) OIDs
 * is supported, via the meta predicate "partial(GOAL,OID)"
 * Moving clause sets up and down
 * (assigning higher/lower priority) is supported here.
 * <br>
 * 
 * 
 * @author <A HREF="mailto:adrian.paschke@gmx.de">Adrian Pasche</A>
 * @author <A HREF="mailto:mandarax@jbdietrich.com">Jens Dietrich</A>
 * @author <A HREF="mailto:a.kozlenkov@city.ac.uk">Alex Kozlenkov</A>
 * @version 2.0 <25 March 2007>
 */
public final class RBSLAKnowledgeBase extends AbstractKnowledgeBase implements IProvaKnowledgeBase {


	// indexed container
	private java.util.Hashtable indexed = new java.util.Hashtable();
	
	// the feature descriptor
	private static KnowledgeBaseFeatureDescriptions featureDescriptions = null;

	// the comparator
	private Comparator comparator = null;

    private Map poly_predicates = new HashMap();

    final static DataSource ds = null;

	
	public class ExtendedClauseSet {
		
		private String key=null;
		private ClauseSet cs=null;
		private Term[] extendedKey=null;
		private Object src = null;  // source id of the clause set
		private Object oid = null;  // source id of the clause set
		public boolean partial = false; // partial reasoning on/off
		private Object partialKey=null; // partial key for partial reasoning
		
		
		public ExtendedClauseSet(ClauseSet _cs, Object _partialKey) {
			
			cs = _cs;
			oid = cs.getProperty("oid");
			src = cs.getProperty("src");
			
			// set partialKey of partial reasoning
			if (_partialKey!=null) {
				partial = true;
				partialKey=_partialKey;
			}
			
			
			if (_cs instanceof ws.prova.reference.RuleImpl) {
				Fact f = ((ws.prova.reference.RuleImpl)_cs).getHead();
				key = f.getKey().toString();
				//src = f.getProperty("src");			
				extendedKey = f.getTerms();				
			}
				
			else if (_cs instanceof org.mandarax.reference.TmpClause) {
				Fact f = ((Fact)((org.mandarax.reference.TmpClause)_cs).negativeLiterals.get(0));
				key = f.getKey().toString();
				//src = f.getProperty("src");				
				extendedKey = f.getTerms();				
			}
			
			else if (_cs instanceof ws.prova.reference.FactImpl) {
				Fact f = (Fact)_cs;
				key = f.getKey().toString();
				//src = f.getProperty("src");				
				extendedKey = f.getTerms();
			}
			
			else  {						
				key = _cs.getKey().toString();
				//src = _cs.getProperty("src");				
				extendedKey = new Term[] {};								
			}			
			
		}
		
		public String getKey() {
			return key;
		}
		
		public Object getSRC() {
			return src;
		}
		
		public Object getOID() {
			return oid;
		}

		public Object getPartialKey() {
			return partialKey;
		}
		
		public void setPartialKey(Object key) {
			partialKey = key;
			if (partialKey==null) partial = false;
			else partial = true;
		}
				
		
		public Term[] getExtendedKey() {
			return extendedKey;
		}
		
		public ClauseSet getClauseSet() {
			return cs;
		}
		
		public String toString() {
			return cs.toString();
		}
		
		/**
		 * Overwrite equals.
		 * Two (extended) ClauseSets are equal if their extended keys partially match each other.
		 * In case of partial reasoning their partial keys must match, too 
		 */
		public boolean equals(Object obj) {		 	
			if (obj==null) return false; 
			
			if (obj instanceof ClauseSet) {
				obj = new ExtendedClauseSet((ClauseSet) obj, null);
			}
			
			if (obj instanceof ExtendedClauseSet) {
				ExtendedClauseSet _cs = (ExtendedClauseSet) obj;
				//if (_cs.getKey().equals(key)) {
						
						// goal demands partial reasoning ? => compare partial keys
				
						if (_cs.partial && !_cs.getPartialKey().equals("")) {							
						
							if (!this.src.equals(_cs.getPartialKey())) {
																
								if (!this.oid.equals(_cs.getPartialKey())) return false;
								
							}
						}		
													
						
						// compare extended keys
						
						Term[] extendedKey2 = _cs.getExtendedKey();
					
						int length=0;
						if (extendedKey.length<extendedKey2.length) length=extendedKey.length;
						else length=extendedKey2.length;

						for (int i=0;i<length;i++) {							
							if ((extendedKey[i] instanceof VariableTermImpl) ||(extendedKey2[i] instanceof VariableTermImpl)) 
								continue;
								//return true;
							if (!extendedKey[i].equals(extendedKey2[i])) {								
								if ((extendedKey[i] instanceof ws.prova.reference.ComplexTermImpl) && (extendedKey2[i] instanceof ws.prova.reference.ComplexTermImpl)) {									
									
									// partially compare complex terms
									boolean t = compareComplex((ComplexTermImpl)extendedKey[i],(ComplexTermImpl)extendedKey2[i]);
									
									if (!t) return false; 									
									//else return true;
									
								} else return false; // no match																																							
							}
						}
						
						return true;
					
				//} 
								
			}
		 
			return false;
			
						
		}
		
		/*
		 * Helper method: Partially compare two complex terms (~fast unification of complex terms)
		 */
		private boolean compareComplex(ComplexTermImpl term1, ComplexTermImpl term2 ) {
			boolean result = (term1.getFunction() == null)
			? term2.getFunction() == null
			: term1.getFunction().equals(term2.getFunction());
			
			if (!result) return false;
			
			Term[] terms1 = term1.getTerms();
			Term[] terms2 = term2.getTerms();

			int length=0;
			if (terms1.length<terms2.length) length=terms1.length;
			else length=terms2.length;			
			for (int i = 0; i < length; i++) {								
				if ((terms1[i] instanceof VariableTermImpl) || (terms2[i] instanceof VariableTermImpl) ) 
					continue;
					//return true;
				if (!terms1[i].equals(terms2[i])) {
					if ((terms1[i] instanceof ws.prova.reference.ComplexTermImpl) && (terms2[i] instanceof ws.prova.reference.ComplexTermImpl)) {				
						boolean res = compareComplex((ComplexTermImpl)terms1[i],(ComplexTermImpl)terms2[i]);
						if (!res) return false;
						//else return true;
					} else return false;
				} 
			}			

			return result;
		}
		
		
		/**
		 * Get the hash code of the extended ClauseSet.
		 *
		 * @return the hash value
		 */
		public int hashCode() {
			int i = (key == null)
					? 0
					: key.hashCode();
			return i;
		}
		
	}
	
	/**
	 * Constructor.
	 */
	public RBSLAKnowledgeBase() {
		super();
	}

	/**
	 * Set a comperator.
	 *
	 * @param comp a comparator
	 */
	public synchronized void setComparator(java.util.Comparator comp) {
		comparator = comp;

		// apply new sorting
		resort();
	}

    /**
     *
     * @param s
     * @param pred
     */
    public synchronized void addPolyPredicate( String s, Predicate pred ) {
       poly_predicates.put(s,pred);
    }

    /**
	 * Resort all entries. Does nothing if the comparator is null.
	 */
	public synchronized void resort() {
		if (comparator != null) {
			for (Iterator keys = getKeys().iterator(); keys.hasNext();) {
				resort(keys.next());
			}
		}
	}

	/**
	 * Resort all entries for a particular key. Does nothing if the comparator is null.
	 * Note: Entries are extended clause sets.
	 * @param key a key (a predicate)
	 */
	public void resort(Object key) {
		if (comparator != null) {
			List clauseSets = (List) indexed.get(key);
			Collections.sort(clauseSets, comparator);
		}
	}

	/**
	 * Get the comperator.
	 *
     */
	public synchronized Comparator getComparator() {
		return comparator;
	}

	/**
	 * Indicates whether the (manual) move operations are enabled.
	 *
	 * @return a boolean
	 */
	public synchronized boolean isMoveEnabled() {
		return comparator == null;
	}

	boolean contains( String s, String substring ) {
		return s.indexOf(substring)>=0;
	}
	
	/**
	 * Add a clause set.
	 *
	 * @param c org.mandarax.kernel.ClauseSet
	 */
	public synchronized void add(ClauseSet c) {	
		

		ExtendedClauseSet cs = getExtendedClauseSet(c,null);		
		String key = cs.getKey();
		LinkedList list = (LinkedList) getOrAddIndexed(cs);		
		if (contains(key,"defeasible")) list.addFirst(cs); // faster access
		else if (contains(key,"happens")) list.addFirst(cs); // faster access
		else if (contains(key,"occurs")) list.addFirst(cs); // faster access
		else list.add(cs);			
		resort(key);

				
			
	}

	/**
	 * Iterate over all clauses
	 *
	 * @return org.mandarax.util.ClauseIterator
	 */
	public synchronized org.mandarax.util.ClauseIterator clauses() {
		return new org.mandarax.util.MultipleClauseSetIterator(getClauseSets());
	}

	/**
	 * Iterate over all clauses found for a certain key.
	 *
	 * @param query               org.mandarax.kernel.Clause
	 * @param additionalParameter java.lang.Object = Property of clauses
	 * @return org.mandarax.util.ClauseIterator
	 */
	public synchronized org.mandarax.util.ClauseIterator clauses(Clause query, Object additionalParameter) {
		
		// create a view on the found clauses	
		ExtendedClauseSet ecs;
		
		// create and return meta lp as fact "metaLP(LP)".
		// Currently, only meta interprets rules and facts
		if (query.getKey().toString().equals("metaLP")) 
			return new org.mandarax.util.MultipleClauseSetIterator(getMetaLP(), query, additionalParameter);
		
		
		//	"partial scoped" reasoning on KB
		
		if (query.getKey().toString().equals("partial") ||
			query.getKey().toString().equals("label")	||
			query.getKey().toString().equals("module")
		) { 	
			Fact f = ((Fact)((org.mandarax.reference.TmpClause)query).negativeLiterals.get(0));			
			// unfold subgoal from partial meta goal and create new fact
			ComplexTerm subGoal = ((ComplexTerm)((ComplexTerm)((ComplexTerm)f.getTerms()[1]).getTerms()[0]).getTerms()[1]);			
			
			String partialKey = null;
			if ((((ComplexTerm)((ComplexTerm)(((ComplexTerm)f.getTerms()[1]).getTerms()[0])).getTerms()[0]).getTerms()[1]) instanceof ConstantTerm) 
			  partialKey  = ((ConstantTerm) (((ComplexTerm)((ComplexTerm)(((ComplexTerm)f.getTerms()[1]).getTerms()[0])).getTerms()[0]).getTerms()[1])).getObject().toString();
																		
            Class[] struct = {Object.class,RList.class};
            String pName = ((ConstantTerm)subGoal.getTerms()[1]).getObject().toString();			
            SimplePredicate p = new SimplePredicate(pName, struct);
            Term[] tmpTerm = {
                    subGoal.getTerms()[1],
                    subGoal};
            FactImpl newFact = new FactImpl(p,tmpTerm);
			
			// set unfolded subgoal
			((org.mandarax.reference.TmpClause)query).negativeLiterals.set(0,newFact);
			
			//	if metaLP subgoal return partial meta LP
			if (subGoal.getTerms()[1].toString().equals("[metaLP]")) 											
				return new org.mandarax.util.MultipleClauseSetIterator(getMetaLP(partialKey), query, additionalParameter);
			
			ecs = getExtendedClauseSet(query,partialKey); // set goal with partial key (scoping)		
			
			// partialKey is variable - bind variable
			if ((((ComplexTerm)((ComplexTerm)(((ComplexTerm)f.getTerms()[1]).getTerms()[0])).getTerms()[0]).getTerms()[1]) instanceof VariableTermImpl) {
				
				((org.mandarax.reference.TmpClause)query).negativeLiterals.set(0,f);
				
				if (query.getKey().toString().equals("label")) {					
					List list = getOrAddIndexed(ecs);
					if (!list.isEmpty()) {
						List tmpList = new ArrayList();
						for (Iterator it = list.iterator();it.hasNext();) {
							ExtendedClauseSet _cs = (ExtendedClauseSet) it.next();							
							if (_cs.equals(ecs)) {
								Replacement rep1 = new Replacement((((ComplexTerm)((ComplexTerm)(((ComplexTerm)f.getTerms()[1]).getTerms()[0])).getTerms()[0]).getTerms()[1]), new ConstantTermImpl(_cs.getOID().toString()));
								Fact f2 = (Fact) f.apply(rep1);
								tmpList.add(f2);
							}							
						}
						
						return new org.mandarax.util.MultipleClauseSetIterator(tmpList, query, additionalParameter);						
					} else
						return new org.mandarax.util.MultipleClauseSetIterator(createView(list), query, additionalParameter);
				}	
				
				if (query.getKey().toString().equals("module")) {
					List list = getOrAddIndexed(ecs);
					if (!list.isEmpty()) {
						List tmpList = new ArrayList();
						for (Iterator it = list.iterator();it.hasNext();) {
							ExtendedClauseSet _cs = (ExtendedClauseSet) it.next();							
							if (_cs.equals(ecs)) {
								Replacement rep1 = new Replacement((((ComplexTerm)((ComplexTerm)(((ComplexTerm)f.getTerms()[1]).getTerms()[0])).getTerms()[0]).getTerms()[1]), new ConstantTermImpl(_cs.getSRC().toString()));
								Fact f2 = (Fact) f.apply(rep1);
								tmpList.add(f2);
							}							
						}
						
						return new org.mandarax.util.MultipleClauseSetIterator(tmpList, query, additionalParameter);						
					} else
						return new org.mandarax.util.MultipleClauseSetIterator(createView(list), query, additionalParameter);
				}
			}	
			
		
		} else ecs = getExtendedClauseSet(query,null); // full reasoning; partial key = null
		
		List list = createView(getOrAddIndexed(ecs),ecs);		
		return new org.mandarax.util.MultipleClauseSetIterator(list, query, additionalParameter);		
	}
	
	public List getMetaLP() {	
		List kb_clauses = getClauseSets(); // all clauses in KB
		return kb2MetaLP(kb_clauses);
	}	
	
	public List getMetaLP(Object partialKey) {
		List kb_clauses = new ArrayList();
		for (Iterator iter = getKeys().iterator(); iter.hasNext();) {
			Object key = iter.next();
			List list = getOrAddIndexed(key);			
			for (ListIterator it = list.listIterator();it.hasNext();) {
				ExtendedClauseSet _cs = (ExtendedClauseSet)it.next();
				if (_cs.getSRC()!=null) 
				if (_cs.getSRC().equals(partialKey))  { // compare partial keys					
					kb_clauses.add(_cs.getClauseSet());					
				}
			}
		}		
		
		return kb2MetaLP(kb_clauses);
	} 

	private List kb2MetaLP(final Collection kb_clauses) {
		
		// translate kb clauses to meta LP
		
		ArrayList metaKB = new ArrayList();
		
								
		for (Iterator it = kb_clauses.iterator();it.hasNext();) {
			
			Object clause = it.next();
			

			if (clause instanceof Fact) {
				
				Fact fact = (Fact) clause;
				ArrayList metaFact = new ArrayList();
				metaFact.add(fact.getTerms()[1]);
				Term tmpFact = (Term) RListUtils.list2RList(metaFact);
				metaKB.add(tmpFact);				

			} else if (clause instanceof Rule) {				
				
				ArrayList metaRule = new ArrayList();
				
				Rule rule = (Rule) clause;					
				Fact head = (Fact) rule.getHead();					
				Term[] headTerms = head.getTerms();					
				Term metaHeadLiteral;
				if (headTerms.length==2) {						
					metaHeadLiteral = (Term) headTerms[1];						
				} else continue;
				
				
				metaRule.add(metaHeadLiteral);
														
				for (Iterator it2 = rule.getBody().iterator();it2.hasNext();) {
					
					Fact bodyLiteral = (Fact) it2.next();							
					Term[] bodyLiteralTerms = bodyLiteral.getTerms();
					Term metaBodyLiteral;
					if (bodyLiteralTerms.length==2) {							
						metaBodyLiteral = (Term) bodyLiteralTerms[1];
					} else continue;
					
					metaRule.add(metaBodyLiteral);
											
				}
				
				Term tmpRule = (Term) RListUtils.list2RList(metaRule);
				metaKB.add(tmpRule);
				
			}
			
		}
							
		ArrayList tmpList = new ArrayList();
		tmpList.add(new ConstantTermImpl("metaLP"));
		VariableTerm x = new VariableTermImpl("LP",Object.class);
		tmpList.add(x);
		Term tmpTerm = (Term) RListUtils.list2RList(tmpList);
		Class[] struct = {Object.class,RList.class};
		SimplePredicate p = new SimplePredicate("metaLP", struct);
		Term[] t = new Term[] {new ConstantTermImpl("metaLP"),tmpTerm};
		Fact metaFact = new FactImpl(p,t);
		Term metaLP = (Term) RListUtils.list2RList(metaKB);
		Replacement rep = new Replacement(x,metaLP);
		Fact substitution = (Fact) metaFact.apply(rep);
		
		ArrayList list = new ArrayList();
		list.add(substitution);	
		
		return list;
	}
	


	/**
	 * Handle a clause set change event.
	 *
	 * @param e org.mandarax.kernel.ClauseSetChangeEvent
	 */
	public void clauseSetChanged(ClauseSetChangeEvent e) {

		// if the predicate has changed, register changed clause
		if ((e.getType() == ClauseSetChangeEvent.KEY_CHANGED) && (e.getSource() instanceof ClauseSet)) {
			ClauseSet cs = (ClauseSet) e.getSource();
			remove(cs);
			add(cs);
		}
	}

	/**
	 * Dump the content. For testing only!
	 */
	public void dump() {
		System.out.println("---------------------------------");
		System.out.println("Dumping Knowldege Base ");


		for (Enumeration en = indexed.keys(); en.hasMoreElements();) {
			Object key = en.nextElement();

			System.out.println("KEY IS : " + key.toString());

			for (Iterator it = getOrAddIndexed(key).iterator(); it.hasNext();) {
				System.out.println(it.next());
			}
		}
	}

	/**
	 * Get a list containing all clause sets.
	 * Note that the collection returned is a read only copy of the
	 * internal collection of clause sets.
	 *
	 * @return java.util.List
	 */
	public synchronized java.util.List getClauseSets() {		
		List list = new ArrayList();
		for (Iterator iter = getKeys().iterator(); iter.hasNext();) {
			Object key = iter.next();
			list.addAll(createView(getOrAddIndexed(key)));
		}
		return list;
	}

	/**
	 * Get a list containing all clause sets that have the key.
	 * New in 2.2 - return copy of the original list.
	 *
	 * @param key object
	 * @return a list containing all clause sets
	 */
	public synchronized java.util.List getClauseSets(Object key) {		
		List list = createView(getOrAddIndexed(getExtendedClauseSet((ClauseSet) key, null)));		
		return list;
	}

	/**
	 * Get the feature descriptions.
	 *
	 * @return org.mandarax.kernel.KnowledgeBaseFeatureDescriptions
	 */
	public KnowledgeBaseFeatureDescriptions getFeatureDescriptions() {
		if (featureDescriptions == null) {
			featureDescriptions = new KnowledgeBaseFeatureDescriptions() {
				protected void initialize() {
					super.initialize();
					this.supported.add(KnowledgeBaseFeatureDescriptions.AUTO_PRIORITIES);
				}
			};
		}

		return featureDescriptions;
	}


	/**
	 * Get the keys.
	 *
	 * @return the keys
	 */
	public java.util.Collection getKeys() {
		return indexed.keySet();
	}


	/**
	 * Either get or add an indexed container for the clause c.
	 *
	 * @param k java.lang.Object
	 * @return a container
	 */
	private List getOrAddIndexed(Object k) {
		/*if ((k instanceof ExtendedClauseSet) && (((ExtendedClauseSet)k).getClauseSet().getKey() instanceof PolyPredicate)) {			
			List result = new ArrayList();
			for (Enumeration en = indexed.keys(); en.hasMoreElements();) {
				Object o = en.nextElement();
				//if (o instanceof String[]) {
					List rhs = (List) indexed.get(o);
					for (int i = 0; i < rhs.size(); i++) {
						ClauseSet o1 = ((ExtendedClauseSet) rhs.get(i)).getClauseSet();
						Fact v = (o1 instanceof Fact) ? ((Fact) o1) : ((Rule) o1).getHead();
						if (v.getProperty("src") != null && !v.getProperty("src").equals("")) {
							result.addAll(rhs);
						}
					}
				//}
			}
			return result;
		}*/ 
		

		List v = null;
		if (k instanceof ExtendedClauseSet) {		
			v = (List) indexed.get(((ExtendedClauseSet) k).getKey());			
		} else v = (List) indexed.get(k);		
							
		if (v==null) {			
			v = new LinkedList();
			if (k instanceof ExtendedClauseSet) indexed.put(((ExtendedClauseSet)k).getKey(), v);
			else indexed.put(k, v);
		}
		return v;
	}


	
	/**
	 * Move a clause set down. (= assign a lower priority)
	 *
	 * @param cs org.mandarax.kernel.ClauseSet
	 */
	public synchronized void moveDown(ClauseSet cs) {

		if (this.isMoveEnabled()) {
			ExtendedClauseSet key = getExtendedClauseSet(cs, null);
			LinkedList list = (LinkedList) getOrAddIndexed(key);
			
			// move down the last matching clause sets
			// note there might be more than one
			for (int i = list.size()-2; i>=0; i--) {
				ExtendedClauseSet ecs = (ExtendedClauseSet) list.get(i);
				if (ecs.getKey().equals(key.getKey())) {
					list.remove(i);
					list.add(i + 1, ecs);
					break;
				}
			}
		} else
			LOG_KB_MOVE.warn("Move operation disabled - clauses are arranged by comparator");
	}

	/**
	 * Move a clause set to the bottom. (= assign the lowest priority)
	 *
	 * @param cs org.mandarax.kernel.ClauseSet
	 */
	public synchronized void moveToBottom(ClauseSet cs) {
		if (this.isMoveEnabled()) {
			ExtendedClauseSet key = getExtendedClauseSet(cs, null);
			LinkedList list = (LinkedList) getOrAddIndexed(key);
			
			// move down the first matching clause set
			// note there might be more than one
			for (int i = list.size()-1; i>=0; i--) {
				ExtendedClauseSet ecs = (ExtendedClauseSet) list.get(i);
				if (ecs.getKey().equals(key.getKey())) {
					list.remove(i);
					list.add(ecs);
					break;
				}
			}			
		} else
			LOG_KB_MOVE.warn("Move operation disabled - clauses are arranged by comparator");
	}

	/**
	 * Move a clause set to the top. (= assign the highest priority)
	 *
	 * @param cs org.mandarax.kernel.ClauseSet
	 */
	public synchronized void moveToTop(ClauseSet cs) {
		if (this.isMoveEnabled()) {
			ExtendedClauseSet key = getExtendedClauseSet(cs, null);
			LinkedList list = (LinkedList) getOrAddIndexed(key);
			
			// move down all matching clause sets
			// note there might be more than one
			for (int i = list.size()-1; i>=0; i--) {
				ExtendedClauseSet ecs = (ExtendedClauseSet) list.get(i);
				if (ecs.getKey().equals(key.getKey())) {
					list.remove(i);
					list.addFirst(ecs);
					break;
				}
			}					
		} else
			LOG_KB_MOVE.warn("Move operation disabled - clauses are arranged by comparator");
	}

	/**
	 * Move a clause set up. (= assign a higher priority)
	 *
	 * @param cs org.mandarax.kernel.ClauseSet
	 */
	public synchronized void moveUp(ClauseSet cs) {
		if (this.isMoveEnabled()) {
			ExtendedClauseSet key = getExtendedClauseSet(cs, null);
			LinkedList list = (LinkedList) getOrAddIndexed(key);
			
			// move down all matching clause sets
			// note there might be more than one
			for (int i = list.size()-1; i>0; i--) {
				ExtendedClauseSet ecs = (ExtendedClauseSet) list.get(i);
				if (ecs.getKey().equals(key.getKey())) {
					list.remove(i);
					list.add(i-1 ,ecs);
					break;
				}
			}		
		} else
			LOG_KB_MOVE.warn("Move operation disabled - clauses are arranged by comparator");
	}

	/**
	 * Remove a clause set.
	 *
	 * @param c org.mandarax.kernel.ClauseSet
	 * @return boolean - indicating whether the object has been found (true) or not (false)
	 */
	public synchronized boolean remove(ClauseSet c) {
		boolean result = remove(c, getExtendedClauseSet(c, null));
		fireKnowledgeBaseChanged(KnowledgeBaseChangeEvent.CLAUSE_REMOVED, c);
		return result;
	}

	/**
	 * Remove a clause set.
	 *
	 * @param c org.mandarax.kernel.ClauseSet
	 * @return boolean - indicating whether the object has been found (true) or not (false)
	 */
	private boolean remove(ClauseSet c, Object key) {		
		List clauses = getOrAddIndexed(key);			
		for (int i = clauses.size()-1; i>=0; i--) {
			ExtendedClauseSet ecs = (ExtendedClauseSet) clauses.get(i);
			if (ecs.getClauseSet().equals(c)) {				
				clauses.remove(i);
				return true;
			}
		}		
		
		return false;
	}

	/**
	 * Remove all clauses.
	 */
	public synchronized void removeAll() {
		indexed = new Hashtable();		
	}

    public Object getPoly(final Object key) {
        return poly_predicates.get(key);
    }

    CalcClauseSet calcClauseSet() {
        CalcClauseSet cs = new CalcClauseSet();
        add(cs);
        return cs;
    }

    CalcPredicate bcalc0 = null;

    /**
     * Constant deterministic built-in predicate
     *
     * @return
     */
    public CalcPredicate bcalc() {
        if( bcalc0!=null )
            return bcalc0;
        CalcClauseSet cs = new CalcClauseSet();
        CalcPredicate p = new CalcPredicate();

        p.setName("bcalc");
        Class[] struct = {
            RList.class};
        p.setStructure(struct);

        cs.setPredicate(p);
        add(cs);
        bcalc0 = p;
        return p;
    }

    CalcPredicate pcalc0 = null;

    public CalcPredicate pcalc() {
        if( pcalc0!=null )
            return pcalc0;
        CalcClauseSet cs = new CalcClauseSet();
        CalcPredicate p = new CalcPredicate();

        p.setName("pcalc");
        Class[] struct = {
            RList.class};
        p.setStructure(struct);

        cs.setPredicate(p);
        add(cs);
        pcalc0 = p;
        return p;
    }

    public SQLExtendedPredicate sqlExtendedPredicate() {
        SQLExtendedClauseSet cs = new SQLExtendedClauseSet();
        SQLExtendedPredicate p = new SQLExtendedPredicate();

        p.setDataSource(ds);
        p.setName("sql");
        Class[] struct = {
            RList.class};
        p.setStructure(struct);
        p.setTypeMapping(new DefaultTypeMapping());

        cs.setSQLPredicate(p);

        add(cs);

        poly_predicates.put(p.getName(), p);
        return p;
    }

    public CalcPredicate calcPredicate(String type) {
        CalcClauseSet cs = new CalcClauseSet();
        CalcPredicate p = new CalcPredicate();

        p.setName(type + "calc");
        Class[] struct = {
            RList.class};
        p.setStructure(struct);

        cs.setPredicate(p);

        add(cs);

        poly_predicates.put(p.getName(), p);
        return p;
    }

    /**
     * A polymorphic predicate that allows creating predicates dynamically
     * for Prova rule bases
     *
     * @param pred String predicate name
     * @param base Predicate the predicate pattern on which
     *             the returned predicate will be based
     *             (normally, derive, conversation, or derivation)
     * @return Predicate the constructed predicate
     */
    public Predicate poly(final String pred, final Predicate base) {
        String key = pred + '+' + base.getName();
        Predicate stored = (Predicate) poly_predicates.get(key);
        if (stored != null)
            return stored;
        Class[] struc = base.getStructure();
        SimplePredicate p = new SimplePredicate(pred, struc);
        poly_predicates.put(key, p);
        return p;
    }

	
	
	
	private ExtendedClauseSet getExtendedClauseSet(ClauseSet c, Object partialKey) {
		
		return new ExtendedClauseSet(c, partialKey);
		
	}
	
	/*
	 *  Create a read only copy of the list. 
	 *  Extended clause sets are translated to normal clause sets.
	 */
	private List createView(List l) {
		return createView(l,null);
	}
	
	/*
	 * Create a read only copy of the list with extended clause sets.
	 * Select all (partial) matching extended clause sets
	 */
	private List createView(List l, ExtendedClauseSet ecs) {
		
		ArrayList rList = new ArrayList();
				
		if (ecs!=null) {			
			for (ListIterator it = l.listIterator();it.hasNext();) {
					ExtendedClauseSet _cs = (ExtendedClauseSet)it.next();										
					if (_cs.equals(ecs))  {						
						rList.add(_cs.getClauseSet());					
					}
			}
			
		} else {
			for (ListIterator it = l.listIterator();it.hasNext();) {				
					rList.add(((ExtendedClauseSet)it.next()).getClauseSet());
			}			
		}
		
		return rList;
		
	}

    
}