package org.mandarax.kernel;

/*
 * 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.Hashtable;
import java.util.Iterator;
import java.util.Map;

/**
 * Represents the result of a query. Comprises information about the derivation (proof)
 * and the replacements. There are some changes in version 3.0: replacements are now organised 
 * in a hash table (for better performance) and there are new constructors building results
 * from result sets (using the current cursor position) in order to support the implementation of result filters.
 * @author <A HREF="mailto:mandarax@jbdietrich.com">Jens Dietrich</A>
 * @version 3.3.1 <9 August 2004>
 * @since 1.0
 */
public class Result extends LObject {

    private Derivation    proof        = null;
    private Map replacementsByKey = new Hashtable();
	private Replacement[] replacements = null;

    /**
     * Constructor.
     * @param d the derivation proving this result
     * @param r the variable replacements
     */
    public Result(Derivation d, Replacement[] r) {
        super ();
        setProof(d);
        setReplacements(r);
    }
	/**
	 * Constructor.
	 * @param r the variable replacements
	 */
	public Result(Replacement[] r) {
		super ();
		setReplacements(r);
	}
	/**
	 * Constructor.
	 * @param r a variable replacement
	 */
	public Result(Replacement r) {
		super ();
		setReplacements(new Replacement[]{r});
	}
	/**
	 * Constructor.
	 * @param term1 a term
	 * @param term2 a term
	 */
	public Result(Term term1,Term term2 ) {
		this(new Replacement(term1,term2));
	}
	/**
	 * Constructor.
	 * @param rs a result set
	 */
	public Result(ResultSet rs) throws InferenceException {
		super ();
		setProof(rs.getProof());
		replacementsByKey.putAll(rs.getResults());		
	}

    /**
     * Get the derivation.
     * @return the proof (derivation)
     */
    public Derivation getProof() {
        return proof;
    }

    /**
     * Get an array of replacements of the input (query) variables.
     * @return an array of replacements
     * @deprecated as from version 3.0
     */
    public Replacement[] getReplacements() {
    	// build if needed
    	if (replacements==null && replacementsByKey!=null) {
    		replacements = new Replacement[replacementsByKey.size()];
    		int index=0;
			Iterator iter = replacementsByKey.values().iterator();
			while (iter.hasNext()) {
				Map.Entry next = (Map.Entry)iter.next();
				replacements[index] = new Replacement((Term)next.getKey(),(Term)next.getValue());
				index = index+1;
			}
    	}
        return replacements;
    }

    /**
     * Get the object that replaces the variable with the
     * given name and type.
     * If there is no such object, return null.
     * @return the object that replaced the variable
     * @param type the type of the variable
     * @param name the name of the variable
     */
    public Object getResult(Class type, String name) {
        VariableTerm term = LogicFactory.getDefaultFactory ().createVariableTerm (name, type);
        return getResult (term);
    }

    /**
     * Get the object that replaces the variable with the name.
     * If there is no such object, return null.<br>
     * <br><b>Warning:</b> This method is convinient and works well
     * under most circumstances, but could lead to mislead�ng results
     * if there are many variables of different types sharing the same
     * name in a query. In this case, better use <code>getResult(Class,String)</code>.
     * @return the object that replaced the variable
     * @param name the name of the variable
     */
    public Object getResult(String name) {
		for(Iterator iter = replacementsByKey.values().iterator();iter.hasNext();) {
			Map.Entry next = (Map.Entry)iter.next();
			Object nextKey = next.getKey();
			if (nextKey instanceof VariableTerm && name.equals(((VariableTerm)nextKey).getName())) {
				return getResult((VariableTerm)nextKey);	
			}
    	}
    	return null;
    }

    /**
     * Get the object that replaces the variable.
     * If there is no such object, return null.
     * @return the object that replaced the variable
     * @param term the variable term that is (perhaps) replaced
     */
    public Object getResult(VariableTerm term) {    	
		Term replacement = (Term)replacementsByKey.get(term);
        if(replacement instanceof ConstantTerm) {
            return((ConstantTerm)replacement).getObject ();
        }
        // by a.kozlenkov@city.ac.uk from 1.9.1: useful when result is complex term
        // still containing parameters
        else return replacement;
    }

    /**
     * Convert the object to a string.
     * @return the string representation of this object
     */
    public String toString() {
        StringBuffer buf   = new StringBuffer ();
        boolean      first = true;
        buf.append ("a result (");
        for(Iterator iter = replacementsByKey.values().iterator();iter.hasNext();) {
        	Map.Entry next = (Map.Entry)iter.next();
            if(first) {
                first = false;
            } else {
                buf.append (',');
            }
            buf.append(next.getKey());
            buf.append("->");
            buf.append(next.getValue());
        }
        buf.append (")");
        return new String (buf);
    }
	/**
	 * Set the derivation.
	 * @param proof
	 */
	protected void setProof(Derivation proof) {
		this.proof = proof;
	}

	/**
	 * Set the replacements.
	 * @param replacements
	 */
	protected void setReplacements(Replacement[] replacements) {
		this.replacements = replacements;
		// organize replacements by key
		replacementsByKey.clear();
		for (int i=0;i<replacements.length;i++) replacementsByKey.put(replacements[i].original,replacements[i].replacement);
	}
	/**
	 * Get the results as map containing term -> term associations.
	 * @return a map 
	 */
	 public Map getResults() throws InferenceException {
	 	return replacementsByKey;
	 }
}
