/*
 * 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
 */
package org.mandarax.util;


import java.io.Serializable;
import java.util.*;

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

/**
 * AutoFacts are generic clause sets. AutoFacts facilitate the task of integrating facts
 * generated from data into the knowledge base at run time (=time when we run the inference engine).<br>
 * It is very useful to subclass <code>AutoFacts</code>, overridding the method <code>getExtension(Class type)</code>.
 * This method returns a collection of instances for the respective type. E.g., assume we have an
 * instance <code>af</code> of such a subclass with <code>getExtension</code> returning
 * (the <code>Integer</code> instances) 1,2 and 3 for <code>Integer.class</code>. Now assume that we perform
 * <code>af.clauses(1&lt;x,null)</code>, where <code>1&lt;x</code> stands for the fact consisting of the
 * <code>IntArithmetic.LESS_THAN</code> predicate, a constant term wrapping the integer 1,
 * and a variable term of the type <code>Integer</code> named "x". Then the iterator returned
 * iterates over two facts: <code>1&lt;2</code> and <code>1&lt;3</code>. If we perform
 * <code>af.clauses(x&lt;y,null)</code>
 * (two variables!), the iterator iterates over three facts <code>1&lt;2</code>,<code>1&lt;3</code>,
 * <code>2&lt;3</code>. This will also work if the parameter(s) contains functions (with a known semantics),
 * e.g., the iterator returned by <code>af.clauses(x*x&lt;x+x,null)</code> (where * and + stand for
 * the respective function defined as static members in <code>IntArithmetic</code>) will iterate over
 * one fact only: <code>1*1&lt;1+1</code>. <br>
 * If <code>clauses()</code> is used without parameters, the set of facts iterated is defined as
 * follows:
 * <ul>
 * <li> The predicate of each fact must be in the array returned by <code>getPredicates()</code>.
 * <li> For each type of any slot of the predicate, the extension
 * is computed, and a fact is build for each combination of these extensions.
 * </ul>
 * E.g., if <code>getPredicates()</code> returns <code>{IntArithmetic.LESS_THAN,IntArithmetic.EQUALS}</code>,
 * and the extension for <code>Integer.class</code> is defined as {1,2,3}, then <code>clauses()</code> returns an
 * iterator iterating over the following facts: <code>1=1</code>,<code>2=2<code> ,<code>3=3</code> ,
 * <code>1&lt;2</code>, <code>1&lt;3</code> and <code>2&lt;3</code>.  <br>
 * In the math test package and in the example.crm package are a couple of examples showing how to use
 * auto facts.
 * @author <A HREF="mailto:mandarax@jbdietrich.com">Jens Dietrich</A>
 * @version 3.3.1 <9 August 2004>
 * @since 1.2
 * @deprecated use JFunction and JPredicate instead. Warning: session support has not been added to AutoFacts
 */
public class AutoFacts extends AbstractPropertiesSupport implements ClauseSet,Serializable {

    protected transient List clauseSetChangeListener = new Vector();
    protected org.mandarax.kernel.Predicate[] predicates;
    private String name = "Automatically built fact base";

    /**
     * Constructor.
     */
    public AutoFacts() {
        super ();
    }

    /**
     * Add a clause set listener.
     * @param l a listener
     */
    public void addClauseSetChangeListener(ClauseSetChangeListener l) {
        clauseSetChangeListener.add (l);
    }

    /**
     * Iterate all clauses. We generate a set of clauses for all predicates
     * returned by <code>getPredicates()</code> only if for all types
     * of any term of this predicate a non empty extension is defined.
     * (see also class comment)
     */
    public org.mandarax.util.ClauseIterator clauses() {
        if(predicates == null) {
            return new ZeroClauseIterator ();
        }

        List allIterators = new ArrayList (predicates.length);
        ClauseIterator nextIterator = null;

        for(int i = 0; i < predicates.length; i++) {
            nextIterator = getClauseIterator (predicates[i]);
            if(nextIterator != null) {
                allIterators.add (nextIterator);
            }
        }

        if(allIterators.isEmpty ()) {
            return new ZeroClauseIterator ();
        } else {
            return new MergedClauseIterator (allIterators);
        }
    }

    /**
     * Get a clause iterator.
     * @return a clause iterator
     * @param query the query clause
     * @param additionalParameter an additional parameter
     */
    public ClauseIterator clauses(org.mandarax.kernel.Clause query,
                                  Object additionalParameter) {
        Fact leadingFact = getFact (query);

        // extract the variables
        Object[]       obj  = extractVariables (leadingFact).toArray ();
        VariableTerm[] vars = new VariableTerm[obj.length];

        System.arraycopy (obj, 0, vars, 0, obj.length);

        // get the extension for each variable
        Collection[] extensions = new Collection[vars.length];

        for(int i = 0; i < extensions.length; i++) {
            extensions[i] = getExtension (vars[i].getType ());
        }

        // iterate over the extensions
        if(vars.length == 0) {
            return new SingleAutoFactIterator (leadingFact, this);
        } else {
            return new AutoFactIterator (
                leadingFact, new MultipleCollectionIterator (extensions),
                vars, this);
        }
    }

    /**
     * Extract the variables from a fact or complex term.
     * @return a collection of variables
     * @param tc a fact or complex term
     */
    private Collection extractVariables(TermContainer tc) {
        Collection coll  = new HashSet ();
        Term[]     terms = tc.getTerms ();
        Term       t;

        for(int i = 0; i < terms.length; i++) {
            t = terms[i];

            if(t.isCompound ()) {
                coll.addAll (extractVariables ((ComplexTerm) t));
            } else {
                if(terms[i].isVariable ()) {
                    coll.add (t);
                }
            }
        }

        return coll;
    }

    /**
     * Fire a clause set change event
     * @param e an event
     */
    protected void fireClauseSetChangeEvent(ClauseSetChangeEvent e) {
        ClauseSetChangeListener l;

        for(Iterator it =
                clauseSetChangeListener.iterator (); it.hasNext (); ) {
            l = (ClauseSetChangeListener) it.next ();

            l.clauseSetChanged (e);
        }
    }

    /**
     * Get a clause iterator for the respective predicate.
     * @return a clause iterator.
     * @param p a predicate
     */
    private ClauseIterator getClauseIterator(Predicate p) {
        Class[]        struct     = p.getStructure ();
        LogicFactory   lfact      = LogicFactory.getDefaultFactory ();
        VariableTerm[] vars       = new VariableTerm[struct.length];
        Collection[]   extensions = new Collection[struct.length];
        Collection     ex         = null;

        for(int i = 0; i < struct.length; i++) {
            ex = getExtension (struct[i]);

            if((ex == null) || ex.isEmpty ()) {
                return null;

                // in this case we cannot build an iterator since we do not know which objects do use in facts
            }

            extensions[i] = ex;
            vars[i]       = lfact.createVariableTerm ("x" + i, struct[i]);
        }

        Fact template = lfact.createFact (p, vars);

        return new AutoFactIterator (
            template, new MultipleCollectionIterator (extensions), vars,
            this);
    }

    /**
     * Get the extension for a type. Note that by default
     * an empty set is returned. This will lead to a clause set
     * containing at most one fact. If the auto fact set should
     * build facts for combinations of variable replacements, then
     * this method needs to be overridden by a subclass.
     * @return a collection of objects of the respective type
     * @param type a type
     */
    protected Collection getExtension(Class type) {
        return new ArrayList ();
    }

    /**
     * Get the fact for a query. The query clause is passed
     * by the inference engine, so we basically implement a selection rule here!
     * @todo .. add a method that takes a selection rules as parameter ?
     * @return org.mandarax.kernel.Fact
     * @param query org.mandarax.kernel.Clause
     */
    private Fact getFact(Clause query) {
        return(Fact) query.getNegativeLiterals ().get (0);
    }

    /**
     * Get the key
     * @return the key (i.e., the predicates for this clause set)
     */
    public Object getKey() {
        return predicates;
    }

    /**
     * Get the name of the auto fact set.
     * @return a name
     */
    public String getName() {
        return name;
    }
    /**
     * Get the predicates.
     * @return the predicates
     */
    public org.mandarax.kernel.Predicate[] getPredicates() {
        return predicates;
    }
	/**
	 * Get an iterator iterating over the predicates contained in this clause set.
	 * @return an iterator
	 */
	public Iterator predicates() {
		if (predicates==null) return new ArrayIterator(new Predicate[]{});
		else return new ArrayIterator(predicates);
	}
    /**
     * Remove a clause set listener.
     * @param l a listener
     */
    public void removeClauseSetChangeListener(ClauseSetChangeListener l) {
        clauseSetChangeListener.remove (l);
    }

    /**
     * Set a name.
     * @param aName a name
     */
    public void setName(String aName) {
        name = aName;
    }

    /**
     * Set the predicates,
     * @param newPredicates the predicates
     */
    public void setPredicates(org.mandarax.kernel.Predicate[] newPredicates) {
        predicates = newPredicates;
    }

    /**
     * Convert the object to a string.
     * @return the string representation of this object
     */
    public String toString() {
        return getName ();
    }
}
