package ws.prova.calc;

/*
 * Copyright (C) 2002-2007 <a href="mailto:a.kozlenkov@city.ac.uk">Alex Kozlenkov</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.AbstractClauseIterator;
import org.mandarax.util.logging.LogCategories;
import ws.prova.RListUtils;
import ws.prova.reference.DefaultLogicFactory;

import java.util.*;

/**
 * Iterator over results returned by Java method calls in Prova
 * <p>Title: Prova</p>
 * <p>Description: Iterator over results returned by Java method calls in Prova</p>
 * <p>Copyright: Copyright (C) 2002-2007</p>
 * <p>Company: City University, London, United Kingdom</p>
 *
 * @author Alex Kozlenkov
 * @version 2.0 <25 March 2007>
 */

public class CalcClauseIterator extends AbstractClauseIterator implements LogCategories {

	private CalcPredicate predicate;
	private Fact nextFact;
	private LogicFactory logFact = DefaultLogicFactory.getDefaultFactory();
	private List head;
	private ListIterator rs;
	private int arity;
	private int size;

	// Prefix to the output (normally comma-separated table names)
	private String listtxt;
	private boolean listtxt_ready = false;
	private List objs = new ArrayList();
	private Fact fact;
	// The original array of parameters in the query
	private Object[] tuple;
	// Substitutions effective for each resulting array of terms
	Vector subs = new Vector();
	private boolean simpleSubstitutions;
	private StringBuffer listsb;

	CalcClauseIterator(final Fact f, final CalcPredicate p, final int arity, final List head, final Object[] tuple, final List res) {
		super();
		fact = f;
		predicate = p;
		this.arity = arity;
		this.head = head;
		this.rs = res.listIterator();
		this.size = res.size();
		this.tuple = tuple;
	}

	public boolean isSimpleSubstitutions() {
		return simpleSubstitutions;
	}

	public int size() {
		return size;
	}

	public List subs() {
		return subs;
	}

	/**
	 * Indicates whether there are more clauses.
	 *
	 * @return true if there are more clauses, false otherwise
	 */
	public boolean hasMoreClauses() {
		while (rs.hasNext()) {
			if (prepareData())
				return true;
			rs.next();
		}
		nextFact = null;
		return false;
	}

	/**
	 * Prepare data for one set of instantiations.
	 *
	 * @return <code>false</code> if conflicts found or fact generation failed, and <code>true</code>, otherwise.
	 */
	protected boolean prepareData() {
		Object[] data = (Object[]) rs.next();
		simpleSubstitutions = true;
		subs.clear();
		Map vars = new HashMap();
		for (int i = 0; i < data.length; i++) {
//            if (tuple[i] instanceof VariableTerm && !(data[i] instanceof Term) && !(data[i] instanceof Object[])) {
//                Replacement r = new Replacement((Term) tuple[i],
//                        ws.prova.util.DefaultLogicFactorySupport.cons(data[i], data[i].getClass()));
//                if (vars.containsKey(r.original)) {
//                    if (vars.get(r.original) != r.replacement)
//                        return false;
//                    continue;
//                }
			if (tuple[i] instanceof VariableTerm && !(data[i] instanceof Object[]) ) {
                Replacement r = null;
                if( data[i] instanceof ComplexTerm ) {
                    if( ((ComplexTerm) data[i]).containsVariables() ) {
                        simpleSubstitutions = false;
                        break;
                    } else {
                        r = new Replacement((Term) tuple[i], (Term) data[i]);
                    }
                } else if ( data[i] instanceof Term ) {
                    simpleSubstitutions = false;
                    break;
                } else
                    r = new Replacement((Term) tuple[i],
						ws.prova.util.DefaultLogicFactorySupport.cons(data[i], data[i].getClass()));
				if (vars.containsKey(r.original)) {
					if (vars.get(r.original) != r.replacement)
						return false;
					continue;
				}
				subs.add(r);
				vars.put(r.original, r.replacement);
			} else if (tuple[i] instanceof Term) {
				simpleSubstitutions = false;
				break;
			}
		}
		if (simpleSubstitutions) {
			return true;
		}
		int listtxt_count = 0;
		Term t = fact.getTerms()[0];
		objs.clear();
		objs.addAll(head);
		if (!listtxt_ready) {
			listsb = new StringBuffer();
			listsb.append("[%0,%1");
			listtxt_count = 2;
		}
		Term[] terms = new Term[1];
		for (int i = 0; i < arity; i++) {
			if (data[i] instanceof Object[]) {
				Object[] res = (Object[]) data[i];
				if (!listtxt_ready) {
					listsb.append(",[");
					for (int k = 0; k < res.length; k++) {
						if (k != 0) {
							listsb.append(',');
						}
						listsb.append('%');
						listsb.append(k + listtxt_count);
					}
					listsb.append(']');
					listtxt_count += res.length;
				}
				for (int n = 0; n < res.length; n++) {
					objs.add(res[n]);
				}
			} else {
				objs.add(data[i]);
				if (!listtxt_ready) {
					listsb.append(",%");
					listsb.append(listtxt_count++);
				}
			}
		}
		if (!listtxt_ready) {
			listsb.append(']');
			listtxt_ready = true;
		}
		listtxt = listsb.toString();
		try {
			terms[0] = RListUtils.rlist(listtxt, objs.toArray());
			nextFact = logFact.createFact(predicate, terms);
			return true;
		} catch (Exception ex) {
			return false;
		}
	}

	/**
	 * Get the next clause.
	 *
	 * @return the next clause
	 * @throws ClauseSetException
	 */
	public Clause nextClause() throws ClauseSetException {
		return nextFact;
	}

}
