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.Collections;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;

import org.mandarax.kernel.Clause;
import org.mandarax.kernel.ClauseSet;
import org.mandarax.kernel.ClauseSetChangeEvent;
import org.mandarax.kernel.KnowledgeBaseChangeEvent;
import org.mandarax.kernel.KnowledgeBaseFeatureDescriptions;

/**
 * Default implementation of a knowledge base.
 * @author <A HREF="mailto:mandarax@jbdietrich.com">Jens Dietrich</A>
 * @version 3.3.1 <9 August 2004>
 * @since 1.0
 */
public final class KnowledgeBase extends AbstractKnowledgeBase {

	// the container. change this to a sorted collection !
	private java.util.Vector container = new java.util.Vector();

	// indexed container
	private java.util.Hashtable indexed = new java.util.Hashtable();

	// the feature descriptor
	private static KnowledgeBaseFeatureDescriptions featureDescriptions = null;

	// event listener
	private transient Vector knowledgeBaseListener = new Vector();

	/**
	 * Constructor.
	 */
	public KnowledgeBase() {
		super();
	}
	/**
	 * Add a clause set.
	 * @param c org.mandarax.kernel.ClauseSet
	 */
	public synchronized void add(ClauseSet c) {
		boolean logEnabled = LOG_KB_ADD.isDebugEnabled();

		container.addElement(c);
		c.addClauseSetChangeListener(this);

		if (logEnabled) {
			LOG_KB_ADD.debug(
				"Added clause " + ((c == null) ? "null" : c.toString()) + " to knowledge base " + toString());
		}

		Object key = c.getKey();

		if (key != null) {
			if (key.getClass().isArray()) {
				Object[] keys = (Object[]) key;

				for (int i = 0; i < keys.length; i++) {
					getOrAddIndexed(keys[i]).addElement(c);

					if (logEnabled) {
						LOG_KB_ADD.debug(
							"Register clause " + ((c == null) ? "null" : c.toString()) + " with index " + keys[i].toString());
					}
				}
			}
			else {
				getOrAddIndexed(key).addElement(c);

				if (logEnabled) {
					LOG_KB_ADD.debug(
						("Register clause " + ((c == null) ? "null" : c.toString()) + " with index " + key == null)
							? "null"
							: key.toString());
				}
			}
		}

		fireKnowledgeBaseChanged(KnowledgeBaseChangeEvent.CLAUSE_ADDED, c);
	}
	/**
	 * Iterate over all clauses.
	 * @return a clause iterator
	 */
	public synchronized org.mandarax.util.ClauseIterator clauses() {
		return new org.mandarax.util.MultipleClauseSetIterator(container);
	}
	/**
	 * Iterate over all clauses found for a certain key.
	 * @return org.mandarax.util.ClauseIterator
	 * @param query a query
	 * @param additionalParameter an additional parameter
	 */
	public org.mandarax.util.ClauseIterator clauses(Clause query, Object additionalParameter) {
		Object key = query.getKey();

		if (key instanceof org.mandarax.kernel.Predicate) {
			return new org.mandarax.util.MultipleClauseSetIterator(
				getOrAddIndexed(key),
				query,
				additionalParameter);
		}
		else {
			return new org.mandarax.util.MultipleClauseSetIterator(new Vector(0));
		}
	}
	/**
	 * Handle a clause set change event.
	 * @param e an event
	 */
	public void clauseSetChanged(ClauseSetChangeEvent e) {

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

			remove(cs);
			add(cs);

			if (LOG_KB_EVENT.isDebugEnabled()) {
				LOG_KB_EVENT.debug(
					("Knowledge base " + toString() + " has handled event " + e == null) ? "" : e.toString());
			}
		}
	}
	/**
	 * 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 a list containing all clause sets
	 */
	public java.util.List getClauseSets() {
		return Collections.unmodifiableList(container);
	}
	/**
	 * Get a list containing all clause sets that have the key.
	 * @param the key object
	 * @return a list containing all clause sets
	 */
	public java.util.List getClauseSets(Object key) {
		return (List) indexed.get(key);
	}
	/**
	 * Get the feature descriptions.
	 * @return the feature descriptions
	 */
	public KnowledgeBaseFeatureDescriptions getFeatureDescriptions() {
		if (featureDescriptions == null) {
			featureDescriptions = new KnowledgeBaseFeatureDescriptions() {
				protected void initialize() {
					super.initialize();
				}
			};
		}

		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.
	 * @return a collection containing clauses with this key
	 * @param k a key object
	 */
	private java.util.Vector getOrAddIndexed(Object k) {
		java.util.Vector v = (java.util.Vector) indexed.get(k);

		if (v == null) {
			v = new java.util.Vector();

			indexed.put(k, v);
		}

		return v;
	}
	/**
	 * Either get or add an indexed container for the clause set c.
	 * @return a collection where to add the clause set
	 * @param c a clause set
	 */
	private java.util.Vector getOrAddIndexed(ClauseSet c) {
		return getOrAddIndexed(c.getKey());
	}
	/**
	 * Remove a clause set.
	 * @return true if the object has been found, false otherwise
	 * @param c the clause set
	 */
	public synchronized boolean remove(ClauseSet c) {
		boolean logEnabled = LOG_KB_REMOVE.isDebugEnabled();
		boolean success = container.removeElement(c);

		c.removeClauseSetChangeListener(this);

		if (logEnabled) {
			LOG_KB_REMOVE.debug(
				"Removed clause " + ((c == null) ? "null" : c.toString()) + " from knowledge base " + toString());
		}

		Object key = c.getKey();

		if (key != null) {
			if (key.getClass().isArray()) {
				Object[] keys = (Object[]) key;

				for (int i = 0; i < keys.length; i++) {
					success = success & getOrAddIndexed(keys[i]).removeElement(c);

					if (logEnabled) {
						LOG_KB_REMOVE.debug(
							("Unregister clause " + ((c == null) ? "null" : c.toString()) + " from index " + key == null)
								? "null"
								: key.toString());
					}
				}
			}
			else {
				success = success & getOrAddIndexed(key).removeElement(c);
			}
		}

		fireKnowledgeBaseChanged(KnowledgeBaseChangeEvent.CLAUSE_REMOVED, c);

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

		for (Iterator it = container.iterator(); it.hasNext();) {
			((ClauseSet) it.next()).removeClauseSetChangeListener(this);
		}

		if (LOG_KB_REMOVE.isDebugEnabled()) {
			LOG_KB_REMOVE.debug("Remove all clauses from knowledge base " + toString());
		}

		fireKnowledgeBaseChanged(
			KnowledgeBaseChangeEvent.CLAUSE_REMOVED,
			Collections.unmodifiableList(container));

		container = new Vector();
	}
}
