package ws.prova.reference;

/*
 * Copyright (C) 1999-2005 <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 org.mandarax.kernel.*;
import org.mandarax.reference.Complex;
import org.mandarax.util.TermIterator;

import ws.prova.RListUtils;

import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Vector;

/**
 * Reference implementation for complex terms.
 *
 * @author <A HREF="mailto:mandarax@jbdietrich.com">Jens Dietrich</A>
 * @author <A HREF="mailto:a.kozlenkov@city.ac.uk">Alex Kozlenkov</A>
 * @version 3.3.1 <9 August 2004>
 * @since 1.1
 *        Prova re-integration modifications
 */
public class ComplexTermImpl extends Complex implements ComplexTerm {

	private Function function = null;

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

	/**
	 * Constructor.
	 *
	 * @param f the function
	 */
	ComplexTermImpl(Function f) {
		super();

		setFunction(f);
	}

	/**
	 * Constructor.
	 *
	 * @param f the function
	 * @param t an array of terms
	 */
	ComplexTermImpl(Function f, Term[] t) {
		super();

		setFunction(f);

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

	/**
	 * Get a term iterator for the subterms, including the term itself.
	 * Looks up the term tree recursively.
	 *
	 * @return a term iterator
	 */
	public TermIterator allSubterms() {
		return new TermIterator(getAllSubterms());
	}

	/**
	 * Apply a replacement to a term.
	 * Prova: see nt.terms[i]
	 *
	 * @param r the replacement
	 * @return the new term resulting from applying the replacement
	 */
	public Term apply(Replacement r) {		
		Term[] t = getTerms();

		// create a new term
		ComplexTermImpl nt = new ComplexTermImpl();

		copyStructure(nt);

		for (int i = 0; i < t.length; i++) {
			nt.terms[i] = t[i].apply(r);
//			nt.setTerm (t[i].apply (r), i);
		}

		return nt;
	}

	public void getAllSubtermsA(List result) {
		result.add(this);
//		for( int i=0; i<terms.length; i++ ) {
		for (int i = terms.length; i-- != 0;) {
			terms[i].getAllSubtermsA(result);
		}
	}

	public void getAllSubtermsA(List result, boolean left) {
		Term[] tt = terms;
		ComplexTerm ct = this;
		do {
			if (left)
				result.add(ct);
			tt[1].getAllSubtermsA(result, true);
			if (tt[0] instanceof ComplexTerm) {
				ct = (ComplexTerm) tt[0];
				tt = ct.getTerms();
			} else {
				tt[0].getAllSubtermsA(result, true);
				tt = null;
			}
		} while (tt != null);

	}


	public boolean getAllSubtermsB(List result, Iterator aux) {
		Term[] tt = terms;
		ComplexTerm ct = this;
		do {
			Object t = ConstantTermImpl.nextNotNull(aux);
			if (t == null) {
				return false;
			}
			if (!(t instanceof ComplexTerm)) {
				result.add(t);
				return false;
			}
			aux.remove();
			if (!tt[1].getAllSubtermsB(result, aux)) {
				return false;
			}
			if (tt[0] instanceof ComplexTerm) {
				ct = (ComplexTerm) tt[0];
				tt = ct.getTerms();
			} else {
				if (!tt[0].getAllSubtermsB(result, aux)) {
					return false;
				}
				tt = null;
			}
		} while (tt != null);
		return true;
	}

	public boolean getAllSubtermsB(List result, Iterator aux, boolean left) {
		if (left) {
			Object t = ConstantTermImpl.nextNotNull(aux);
			if (t == null) {
				return false;
			}
			if (!(t instanceof ComplexTerm)) {
				result.add(this);
				return false;
			}
			aux.remove();
		}
		for (int i = terms.length; i-- > 0;) {
			left = i != 0;
			if (!terms[i].getAllSubtermsB(result, aux, left)) {
				return false;
			}
		}
		return true;
	}

	/**
	 * Indicates whether the term containes variables.
	 *
	 * @return true if the term contains a variable, false otherwise. Looks up the term tree recursively.
	 */
	public boolean containsVariables() {
		for (TermIterator e = terms(); e.hasMoreTerms();) {
			if (e.nextTerm().containsVariables()) {
				return true;
			}
		}

		return false;
	}

	/**
	 * Indicates whether the term contains the provided variable term.
	 *
	 * @param var a variable term
	 * @return true if the term contains the variable term provided, false otherwise
	 */
	public boolean containsVariable(VariableTerm var) {
		for (int i = terms.length; i-- != 0;) {
			if (terms[i].containsVariable(var)) {
				return true;
			}
		}

		return false;
	}

	/**
	 * Copy the structure, i.e. length of the terms, types of the terms and methods
	 * into the term passed as parameter.
	 *
	 * @param t another complex term that takes the structure from this term
	 */
	private void copyStructure(ComplexTermImpl t) {
		t.types = types;
		t.terms = new Term[terms.length];
		t.function = function;
	}

	/**
	 * 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 ComplexTermImpl)) {
			ComplexTermImpl t = (ComplexTermImpl) obj;
			boolean result = (function == null)
					? t.function == null
					: function.equals(t.function);
			Term[] terms1 = terms;
			Term[] terms2 = t.terms;

			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;
	}

	/**
	 * Get an array comprising all subterms, including the term itself.
	 * Looks up the term tree recursively.
	 *
	 * @return an array of terms
	 */
	public Term[] getAllSubterms() {
		Vector t = new Vector();
		Term c = null;

		t.addElement(this);

		for (TermIterator e = terms(); e.hasMoreTerms();) {
			c = e.nextTerm();

			for (TermIterator ec = c.allSubterms(); ec.hasMoreTerms();) {
				t.addElement(ec.nextTerm());
			}
		}

		// copy collected terms in an array
		Term[] arr = new Term[t.size()];

		t.copyInto(arr);

		return arr;
	}

	/**
	 * Get the object constructing the complex.
	 *
	 * @return the function of this term
	 */
	protected Constructor getConstructor() {
		return getFunction();
	}

	/**
	 * Get the function.
	 *
	 * @return org.mandarax.kernel.Function
	 */
	public Function getFunction() {
		return function;
	}

	/**
	 * Get the type of the term.
	 *
	 * @return the term type
	 */
	public Class getType() {
		return getFunction().getReturnType();
	}

	/**
	 * Get the hash code of a fact.
	 *
	 * @return the hash value
	 */
	public int hashCode() {
		int i = (function == null)
				? 0
				: function.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 term is compound.
	 *
	 * @return true
	 */
	public boolean isCompound() {
		return true;
	}

	/**
	 * Indicates whether the term is a constant.
	 *
	 * @return false
	 */
	public boolean isConstant() {
		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 ((function != null) && function.isExecutable()) {
			Term[] t = getTerms();

			for (int i = 0; i < t.length; i++) {
				if (!t[i].isExecutable()) {
					return false;
				}
			}

			return true;
		}

		return false;
	}

	/**
	 * Indicates whether the term is a variable.
	 *
	 * @return false
	 */
	public boolean isVariable() {
		return false;
	}

	/**
	 * Resolve a complex term: if supported, the function
	 * should be performed using its semantic. E.g., for a complex term
	 * like <i>color(window)</i> the function <i>color</i> is performed for the <i>window</i>
	 * object and the result (e.g., <i>white</i>) is returned.
	 * Note that not all functions support this (since not all functions
	 * have a semantic), and the operation can also fail
	 * in case <i>window</i> is a variable term. In these cases we throw an exception.
	 *
	 * @param session a session object
	 * @return the result of resolving the term
	 * @throws UnsupportedOperationException
	 * @throws IllegalArgumentException
	 */
	public Object resolve(Session session) throws UnsupportedOperationException, IllegalArgumentException {
		return function.perform(terms, session);
	}

//	/**
//	 * Resolve a complex term: if supported, the function
//	 * should be performed using its semantic. E.g., for a complex term
//	 * like <i>color(window)</i> the function <i>color</i> is performed for the <i>window</i>
//	 * object and the result (e.g., <i>white</i>) is returned.
//	 * Note that not all functions support this (since not all functions
//	 * have a semantic), and the operation can also fail
//	 * in case <i>window</i> is a variable term. In these cases we throw an exception.
//	 * @return the result of resolving the term
//	 * @throws java.lang.UnsupportedOperationException
//	 * @throws java.lang.IllegalArgumentException
//	 */
//	public Object resolve()
//			throws UnsupportedOperationException, IllegalArgumentException {
//		return null;//return function.perform (terms);
//	}

	/**
	 * Indicates whether the object is the same as the parameter.
	 * This function does not compare subterms but only the functions !
	 *
	 * @param t another term
	 * @return true if the both objects are the same
	 */
	public boolean sameAs(Term t) {
		if (!(t instanceof ComplexTerm)) {
			return false;
		}

		return (getFunction().equals(((ComplexTerm) t).getFunction()));
	}

    /**
     * Set a new function.
     *
     * @param f the function
     */
    public void setFunction(Function f) {
        function = f;
        terms = new Term[f.getStructure().length];
        types = f.getStructure();
    }

	/**
	 * Convert the receiver to a string.
	 * @return the string representation of this object
	 */
	public String toString() {
		String output;
//		if (cachedToString == null) {
//			List list = new ArrayList();
//			RListUtils.rlist2List(this, list, 0);
			StringBuffer buf = new StringBuffer ();
			buf.append(RListUtils.term_as_string(this));

//			buf.append (getConstructor ().getName ());
//			buf.append ("(");
//
//			boolean first = true;
//
//			for(TermIterator e = terms (); e.hasMoreTerms (); ) {
//				if(first) {
//					first = false;
//				} else {
//					buf.append (",");
//				}
//
//				Term nextTerm = e.nextTerm ();
//
//				buf.append ((nextTerm == null)
//							? "null"
//							: nextTerm.toString ());
//			}
//
//			buf.append (")");

			output = new String (buf);
//		}
		return output;
	}

	/**
     * Apply a minimal (least intrusive) substitution to a term.
     * @return the term resulting from applying the replacement
     * @param r a replacement
     */
	public Term applyMinimally(Collection r, Map complexReplacements) {
		if( complexReplacements.containsKey(this) ) {
			return (Term) complexReplacements.get(this);
		}
		boolean changed = false;
		Term[] newTerm = new Term[getTerms().length];
		for( int i=0; i<getTerms().length; i++ ) {
			newTerm[i] = getTerms()[i].applyMinimally(r,complexReplacements);
			if( newTerm[i]!=getTerms()[i] )
				changed = true;
		}
		if( changed ) {
			// create a new term
			ComplexTermImpl nt = new ComplexTermImpl();
			copyStructure(nt);
			nt.setTerms(newTerm);
			complexReplacements.put(this, nt);
			return nt;
		}
		return this;
	}

}
