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 org.mandarax.kernel.*;
import org.mandarax.util.TermIterator;

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( org.mandarax.kernel.Function f ) {
		super();

		setFunction(f);
	}

	/**
	 * Constructor.
	 *
	 * @param f the function
	 * @param t an array of terms
	 */
	ComplexTermImpl( org.mandarax.kernel.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 org.mandarax.kernel.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 org.mandarax.kernel.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 java.lang.UnsupportedOperationException
	 *
	 * @throws java.lang.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 org.mandarax.kernel.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();
    }

	public Term applyMinimally(Collection r, Map complexReplacements) {
		// TODO Auto-generated method stub
		return null;
	}
}
