/*
 * Copyright (C) 1998-2002 <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.reference;

import java.util.*;
import org.mandarax.kernel.*;

/**
 * Default session implementation.
 * @author <A HREF="mailto:mandarax@jbdietrich.com">Jens Dietrich</A>
 * @version 3.3.1 <9 August 2004>
 * @since 3.2
 */

public class SessionImpl implements Session, org.mandarax.util.logging.LogCategories {
	private Object id = null;
	private Map attributes = null;
	private org.mandarax.kernel.KnowledgeBase kb = null;
	private InferenceEngine ie = null;
	private Query query = null;
	private Map variableReplacements = null;
	private List queryVariables = null;
	private List lastConstraintPool = null;
	private Map lastVariableRenamings = null;
	private int exceptionHandlingStrategy = InferenceEngine.BUBBLE_EXCEPTIONS;
	private int cardinalityConstraint = InferenceEngine.ALL;
	/**
	 * Constructor.
	 * @param kb a knowledge base
	 * @param ie an inference engine
	 * @param query a query
	 * @param exceptionHandlingStrategy the exception handling strategy
	 * @param cardinality constraint the cardinality constraint
	 */
	public SessionImpl(org.mandarax.kernel.KnowledgeBase kb, InferenceEngine ie, Query query,int exceptionHandlingStrategy, int cardinalityConstraint) {
		super();
		this.kb = kb;
		this.ie = ie;
		this.query = query;
		this.exceptionHandlingStrategy = exceptionHandlingStrategy;
		this.cardinalityConstraint = cardinalityConstraint;
		queryVariables = IEUtils.getVars(query);
	}
	
	/**
	 * Get a unique session id.
	 * @return a session id
	 */
	public Object getId() {
		if (id==null) id = new java.rmi.server.UID();
		return id;
	}
	/**
	 * Set (bind) a session attribute.
	 * @param attrName the attribute name
	 * @param attrValue an attribute value
	 */
	public void setAttribute(Object attrName,Object attrValue) {
		getAttributes().put(attrName,attrValue);
	}
	/**
	 * Get (lookup) a session attribute.
	 * @param attrName the attribute name
	 * @return the attribute value (null indicates that there was no registered value.
	 */
	public Object getAttribute(Object attrName) {
		return getAttributes().get(attrName);
	}
	/**
	 * Get the attributes.
	 * @return the attribute map.
	 */
	private Map getAttributes() {
		if (attributes==null) attributes=new Hashtable();
		return attributes;
	}



	/**
	 * Compares objects.
	 * @param obj another object
	 * @return a boolean
	 */
	public boolean equals(Object obj) {
		if (obj instanceof SessionImpl) return getId().equals(((SessionImpl)obj).getId());
		return false;
	}

	/**
	 * Get the hashcode for this object.
	 * @return an integer hash value
	 */
	public int hashCode() {
		return getId().hashCode();
	}
	
	/**
	 * Get the knowledge base used in this session.
	 * @return a knowledge base
	 */
	public org.mandarax.kernel.KnowledgeBase getKnowledgeBase()  {
		return kb;
	}
	/**
	 * Get the inference engine used in this session.
	 * @return an inference engine
	 */ 
	public InferenceEngine getInferenceEngine() {
		return ie;
	}
	/**
	 * Get the query for this session.
	 * @return a query
	 */
	public Query getQuery() {
		return query;
	}
	
	/**
	 * Get the replacement value for a variable.
	 * @param varRenamings a map of variable renamings
	 * @param constraintPoll a constraint pool
	 */
	private Term resolve(VariableTerm var) {
		// TODO optimize
		Term replacement = resolveWithConstraints(var);
		if (replacement instanceof ConstantTerm) return replacement;
		if (replacement==null) replacement =  (VariableTerm)lastVariableRenamings.get(var);
		if (replacement!=null) {
			return resolve((VariableTerm)replacement);
		} 
		return null;
		
	}
	/**
	 * Get the replacement value for a variable.
	 * @param var a variable
	 */
	private Term resolveWithConstraints(VariableTerm var) {
		if (lastConstraintPool==null) return null;
		for (int i=0;i<lastConstraintPool.size();i++) {
			Replacement r = (Replacement)lastConstraintPool.get(i);
			if (r.original.equals(var)) return r.replacement;
		}
		return null;		
	}
	/**
	 * Get a map containing the current query variable replacements. 
	 * I.e., the map contains association VariableTerm -> ConstantTerm
	 * @return a map
	 */
	public Map getVariableReplacements() {
		if (variableReplacements==null) {
			variableReplacements = new Hashtable();
			// protect this block against runtime exceptions 
			try {
				for (int i=0;i<queryVariables.size();i++) {
					VariableTerm var = (VariableTerm)queryVariables.get(i);
					Object con = resolve(var);
					variableReplacements.put(var,con);
				}
			}
			catch (Throwable x) {
				LOG_IE.error("Error building variable replacement map for derivation event listener",x);
			}
		}
		return variableReplacements;
	}
	/**
	 * Update the variable renamings and the constraints.
	 * @param a map containing variable renamings (var term -> var term associations)
	 * @param a list of constraints 
	 */
	void update(Map variableRenamings,List constraints) {
		this.lastConstraintPool = constraints;
		this.lastVariableRenamings = variableRenamings;
		// computing the actual replacements might be expensive and is therefore
		// done using lazy initialization
		this.variableReplacements = null;
	}
	/**
	 * Get the exception handling strategy used in this session.
	 * @return an integer
	 * @see InferenceEngine.BUBBLE_EXEPTIONS
	 * @see InferenceEngine.TRY_NEXT
	 */
	public int getExceptionHandlingStrategy() {
		return exceptionHandlingStrategy;
	}
	/**
	 * Get the cardinality constraints for this session.
	 * @return an integer
	 * @see InferenceEngine.ALL
	 */
	public int getCardinalityContraint() {
		return cardinalityConstraint;
	}

}
