package org.mandarax.reference;

/*
 * 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.*;

import org.apache.commons.collections.iterators.SingletonIterator;
import org.mandarax.kernel.*;

/**
 * Reference implementation for (simple) facts. In older versions (prior to 1.9)
 * there was no separate Query interface/class - facts were used instead.
 * In order to facilitate maintanance of older applications, this class still implements
 * Query. Some newer features, in particular DerivationEventListeners, are not supported.
 * For queries, QueryImpl should be used instead.
 * @see QueryImpl
 * @author <A HREF="mailto:mandarax@jbdietrich.com">Jens Dietrich</A>
 * @version 3.3.1 <9 August 2004>
 * @since 1.1
 * Prova re-integration modifications
 * @author <A HREF="mailto:a.kozlenkov@city.ac.uk">Alex Kozlenkov</A>
 * @version 3.3.1 <9 August 2004>
 */
public class FactImpl extends Complex implements Fact,Query {

	private java.util.List posLiterals = null;
	private static java.util.List negLiterals = new java.util.Vector();
	private Predicate predicate = null;
	private transient Vector clauseSetChangeListener = new Vector();
	private ClauseSet container = this;
	// utility vars for the query interface
	private Fact[] facts = {this};
	private String name = null;
	private Properties delegate = null;
	private int containsVariables = -1;

	/**
	 * Constructor.
	 */
	public FactImpl() {
		super ();
	}
	/**
	 * Constructor.
	 * @param p the predicate
	 */
	FactImpl(org.mandarax.kernel.Predicate p) {
		super ();
		setPredicate (p);
	}
	/**
	 * Constructor.
	 * @param p the predicate
	 * @param t the terms
	 */
	FactImpl(org.mandarax.kernel.Predicate p, Term[] t) {
		super ();
		setPredicate (p);
		for(int i = 0; i < t.length; i++) {
			setTerm (t[i], i);
		}
	}
	/**
	 * Add a clause set listener.
	 * @param l a listener
	 */
	public void addClauseSetChangeListener(ClauseSetChangeListener l) {
		clauseSetChangeListener.add (l);
	}
	/**
	 * Apply a set of replacements to a clause. Returns a new clause!
	 * @return the fact (clause) resulting from the application of the replacements
	 * @param r a collection of replacements
	 */
	public Clause apply(java.util.Collection r) {
		return applyToFact (r);
	}
	/**
	 * Apply a single replacement to a fact. Returns a new clause!
	 * @return the fact (clause) resulting from the application of the replacemen
	 * @param r the replacement
	 */
	public Clause apply(Replacement r) {
		return applyToFact (r);
	}
	/**
	 * Apply a set of replacements to a fact. Returns a new fact!
	 * @return the fact resulting from the application of the replacement
	 * @param r a collection of replacement
	 */
	public Fact applyToFact(java.util.Collection r) {
		Term[] t       = getTerms ();
		Term   newTerm = null;

		// create a new fact
		FactImpl f = new FactImpl ();
		copyStructure (f);
		for(int i = 0; i < t.length; i++) {
			newTerm = t[i];
			for(java.util.Iterator it = r.iterator (); it.hasNext (); ) {
				newTerm = newTerm.apply ((Replacement) it.next ());
			}
			f.setTerm (newTerm, i);
		}

		return f;
	}
	/**
	 * Apply a single replacement to a fact. Returns a new fact!
	 * @return the fact (clause) resulting from the application of the replacement
	 * @param r a replacement
	 */
	public Fact applyToFact(Replacement r) {
		Term[] t = getTerms ();
		// create a new fact
		FactImpl f = new FactImpl ();
		copyStructure (f);
		for(int i = 0; i < t.length; i++) {
			f.setTerm (t[i].apply (r), i);
		}
		return f;
	}

	/**
	 * Whether the fact contains variables
	 * @return boolean
	 */
	public boolean isBound() {
		if( containsVariables >= 0 )
			return containsVariables==0;

		Term[] tt = this.getTerms();
		for( int i=0; i<tt.length; i++ ) {
			if( tt[i].containsVariables() ) {
				containsVariables = 1;
				return false;
			}
		}
		containsVariables = 0;
		return true;
	}

	/**
	 * Get an iterator for clauses.
	 * @return a clause iterator
	 */
	public org.mandarax.util.ClauseIterator clauses() {
		return new org.mandarax.util.SingleClauseIterator (this);
	}
	/**
	 * Get an iterator for clauses. The parameters are ignored and <code>clauses()</code> is called!
	 * @return a clause iterator
	 * @param query a query
	 * @param additionalParameter an additional parameter
	 */
	public org.mandarax.util.ClauseIterator clauses(Clause query,Object additionalParameter) {
		return clauses ();
	}
	/**
	 * Copy the structure, i.e. length of the terms, types of the terms and methods,
	 * @param f the fact that takes the structure from this object
	 */
	protected void copyStructure(FactImpl f) {
		f.types     = types;
		f.terms     = new Term[terms.length];
		f.predicate = predicate;
	}
	/**
	 * Check whether objects are equal.
	 * @param obj the object to compare this object with
	 * @return true if the objects are equal, false otherwise
	 */
	public boolean equals(Object obj) {
		if((obj != null) && (obj instanceof FactImpl)) {
			FactImpl f      = (FactImpl) obj;
			boolean  result = (predicate == null)
							  ? f.predicate == null
							  : predicate.equals (f.predicate);
			Term[]   terms1 = terms;
			Term[]   terms2 = f.terms;
			if (terms1==null) result = result && terms2==null;
			else {
				for(int i = 0; i < terms.length; i++) {
					result = result
							 && (((terms[i] == null) && (terms2[i] == null))
								 || (terms1[i].equals (terms2[i])));

					if( !result) {
						return false;
					}
				}
			}
			return result;
		}
		return false;
	}
	/**
	 * Fire a clause set change event
	 * @param e the event
	 */
	protected void fireClauseSetChangeEvent(ClauseSetChangeEvent e) {
		ClauseSetChangeListener l;

		for(Iterator it = clauseSetChangeListener.iterator (); it.hasNext (); ) {
			l = (ClauseSetChangeListener) it.next ();
			l.clauseSetChanged (e);
		}
	}
	/**
	 * Get the object constructing the complex.
	 * @return the predicate of this fact
	 */
	protected org.mandarax.kernel.Constructor getConstructor() {
		return getPredicate ();
	}
	/**
	 * Get the clause set containing the clause. This method is in particular useful for analyzing derivations,
	 * since derivations show only the clauses used, not the clause set generating this clauses (e.g., AutoFacts). On the other
	 * hand, knowledge bases contain often clause sets. So it could be hard to find the clause sets in the knowledge base
	 * that caused a certain result. For some clauses such as facts, the container is just the fact itself.
	 * @return a clause set
	 */
	public ClauseSet getContainer() {
		return container;
	}
	/**
	 * Get a key for fast access. The key is usually the predicate, but subclasses
	 * might want to use different keys. Note that keys are mainly used by inference engines and
	 * knowledge bases to improve performance and inference engine and knowledge bases
	 * must know about the semantics of keys to use them correctly!
	 * @return the key object
	 */
	public Object getKey() {
		return getPredicate ();
	}
	/**
	 * Get the negative literals.
	 * @return the collection of negative literals, this is an empty collection!
	 */
	public java.util.List getNegativeLiterals() {
		return negLiterals;
	}
	/**
	 * Get the positive literals.
	 * @return the collection of positive literals, this is a singleton containing only this object
	 */
	public java.util.List getPositiveLiterals() {
		if(posLiterals == null) {
			posLiterals = new java.util.Vector (1);
			posLiterals.add (this);
		}

		return posLiterals;
	}
	/**
	 * Get the predicate.
	 * @return the predicate
	 */
	public org.mandarax.kernel.Predicate getPredicate() {
		return predicate;
	}
	/**
	 * Get the hash code of a fact.
	 * @return the hash value
	 */
	public int hashCode() {
		int i = (predicate == null)
				? 0
				: predicate.hashCode ();

		if(terms.length == 0) {
			return i;
		}

		if(terms.length == 1) {
			return i ^ ((terms[0] == null)
						? 0
						: terms[0].hashCode ());
		} else {
			return i ^ ((terms[0] == null)
						? 0
						: terms[0].hashCode ()) ^ ((terms[terms.length - 1]
													== null)
												   ? 0
												   : terms[terms.length - 1]
													   .hashCode ());
		}
	}
	/**
	 * Indicates whether the clause is atomic.
	 * @return a boolean
	 */
	public boolean isAtomic() {
		return true;
	}
	/**
	 * Indicates whether the clause is the empty clause.
	 * @return false
	 */
	public boolean isEmpty() {
		return false;
	}
	/**
	 * Indicates whether the object (usually a term or a clause set) can be performed
	 * using the java semantics.
	 * @return a boolean
	 */
	public boolean isExecutable() {
		if((predicate != null) && predicate.isExecutable ()) {
			Term[] t = getTerms ();
			for(int i = 0; i < t.length; i++) {
				if( !t[i].isExecutable ()) {
					return false;
				}
			}

			return true;
		}

		return false;
	}
	/**
	 * Remove a clause set listener.
	 * @param l a listener
	 */
	public void removeClauseSetChangeListener(ClauseSetChangeListener l) {
		clauseSetChangeListener.remove (l);
	}
	/**
	 * Resolve a fact: if supported, the predicate
	 * should be performed using its semantic. E.g., for a fact
	 * like <i>isFather(Max,Jens)</i> the function <i>isFather</i> is performed
	 * for the objects (constant terms) <i>Max</i> and <i>Jens</i> and the result (e.g., <i>true</i>) is returned.
	 * Note that not all predicates support this (since not all predicates
	 * have a semantic), and the operation can also fail
	 * in case one term is variable. In these cases we throw appropriate exceptions.
	 * @param session a session object
	 * @return the result of resolving the term
	 * @throws java.lang.UnsupportedOperationException
	 * @throws java.lang.IllegalArgumentException
	 */
	public Object resolve(Session session) throws UnsupportedOperationException, IllegalArgumentException {
		return predicate.perform (terms,session);
	}
	/**
	 * Set a container.
	 * @param aContainerthe new container
	 */
	public void setContainer(ClauseSet aContainer) {
		container = aContainer;
	}
	/**
	 * Set a new predicate.
	 * @param p the predicate
	 */
	public void setPredicate(org.mandarax.kernel.Predicate p) {
		Predicate oldPredicate = predicate;
		predicate = p;
		terms     = new Term[p.getStructure ().length];
		types     = p.getStructure ();
		if((oldPredicate == null)
		   ? p != null
		   : !(oldPredicate.equals (p))) {
			fireClauseSetChangeEvent (new ClauseSetChangeEvent (this,
					oldPredicate, p, ClauseSetChangeEvent.KEY_CHANGED));
		}
	}
	/**
     * Get the facts.
     * @see org.mandarax.kernel.Query
     * @return an array of facts
     */
    public Fact[] getFacts() {
    	return facts;
    }
    /**
     * Set the facts. Not supported by facts, throws an exception!
     * Please use QueryImpl for queries!
     * @see org.mandarax.kernel.Query
     * @param facts an array of facts
     */
    public void setFacts(Fact[] facts) {
    	throw new UnsupportedOperationException("FactImpl does not support setFacts(). Please use QueryImpl for queries!");
    }

    /**
     * Get the common name of this query, i.e., something like "Find the oncle of a person".
     * @return a string
     */
    public String getName() {
    	return name;
    }

    /**
     * Set the common name of this query.
     * @param a name
     */
    public void setName(String queryName) {
    	name = queryName;
    }

    /**
	 * Indicates whether the fact is negated (negation as failure)
	 * @return always false
	 * @see PrerequisiteImpl
	 */
	public boolean isNegatedAF() {
		return false;
	}
		/**
	 * Set a property.
	 * @param key the key
	 * @param value the value
	 * @return the previous value of the specified key in this property list, or null if it did not have one.
	 */
	public Object setProperty(String key,String value) {
		if (delegate==null) delegate = new Properties();
		return delegate.setProperty(key,value);
	}
	/**
	 * Get a property.
	 * @param key the property key
	 * @return the respective value. The method returns null if the property is not found.
	 */
	public String getProperty(String key) {
		if (delegate==null) delegate = new Properties();
		return delegate.getProperty(key);
	}
	/**
	 * Returns an enumeration of all the keys in this property list, including distinct
	 * keys in the default property list if a key of the same name has not already been
	 * found from the main properties list.
	 * @return an enumeration of all the keys in this property list, including the keys in
	 * the default property list
	 */
	public Enumeration propertyNames() {
		if (delegate==null) delegate = new Properties();
		return delegate.propertyNames();
	}
	/**
	 * Remove a property.
	 * @param key the property key
	 * @return the value to which the key had been mapped, or null if the key did not have a mapping.
	 */
	public Object removeProperty(String key) {
		if (delegate!=null) return delegate.remove(key);
		return null;
	}
	/**
	 * Get the properties as one "properties" instance.
	 * @return a properties instance
	 */
	public Properties getProperties() {
		if (delegate==null) delegate = new Properties();
		return delegate;
	}
	/**
	 * Set the properties. Not required by the interface, but useful for bean (introspection-) based
	 * tools.
	 * @param properties the properties
	 */
	public void setProperties(Properties properties) {
		delegate = properties;
	}
	/**
	 * Get an iterator iterating over the predicates contained in this clause set.
	 * @return an iterator
	 */
	public Iterator predicates() {
		return new SingletonIterator(predicate);
	}
	/**
	 * Get the registered derivation event listeners.
	 * @return an array of listeners
	 */
	public DerivationEventListener[] getDerivationEventListeners() {
		throw new UnsupportedOperationException("Please use QueryImpl instead");
	}
	/**
	 * Set the derivation event listeners.
	 * @param listeners listeners
	 */
	public void setDerivationEventListeners(DerivationEventListener[] listeners) {
		throw new UnsupportedOperationException("Please use QueryImpl instead");
	}
	public Fact applyToFactMinimally(Collection r, Map complexReplacements) {
		// TODO Auto-generated method stub
		return null;
	}
}
