package ws.prova.sql;

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

import java.sql.Connection;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;

/**
 * Iterator over Prova SQL results
 * <p>Title: Prova</p>
 * <p>Description: Iterator over Prova SQL results</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 SQLExtendedClauseIterator extends AbstractClauseIterator implements LogCategories {

	private SQLPredicate predicate;
	private java.sql.ResultSet rs = null;
	private Fact nextFact = null;
	private LogicFactory logFact = DefaultLogicFactory.getDefaultFactory();
//	private Class[] struct = null;
	private List cache = null;
	private boolean closeConnection = false;
	private Connection connection = null;
	private int ncols = 0;
	private Fact fact = null;
	private Object[] columns = null;
	public ArrayList subs = null;
	private Term tail;
	private int tailStartsAt;
	private Statement stmt;
	private boolean exceptionWithHnadler;

	/**
	 * Constructor
	 *
	 * @param f                    Fact
	 * @param field2var            List
	 * @param p                    SQLPredicate the predicate
	 * @param exceptionWithHandler Whether an SQLException has occurred during the query
	 * @param queryResult          ResultSet the result set
	 * @param c                    List the cache list to collect facts - if null, nothing will be cached
	 * @param closeConnection      boolean whether to close the connection at the end
	 * @param connection           Connection the connection used
	 * @param tailStartsAt         int
	 * @param tailVar              String
	 */
	SQLExtendedClauseIterator(Fact f, List field2var, SQLPredicate p,
							  boolean exceptionWithHandler, java.sql.ResultSet queryResult,
							  List c, boolean closeConnection, Statement stmt,
							  Connection connection, int tailStartsAt, String tailVar) {
		super();
		this.exceptionWithHnadler = exceptionWithHandler;
		this.stmt = stmt;
		if (queryResult == null) {
			return;
		}
		fact = f;
		predicate = p;
		rs = queryResult;
//		struct = predicate.getStructure();
		cache = c;
		this.closeConnection = closeConnection;
		this.connection = connection;
		ncols = 0;
		try {
			ResultSetMetaData meta = rs.getMetaData();
			ncols = meta.getColumnCount();
			String varname = null;
			columns = new Object[ncols];

			subs = new ArrayList(field2var.size());

			for (int col = 0; col < ncols; col++) {
				Object o = field2var.get(col);
				if (o == null) {
					columns[col] = null;
					continue;
				}
				varname = (String) o;
				columns[col] = logFact.createVariableTerm(varname, Object.class);
				if (columns[col] != null) {
					subs.add(new Replacement((VariableTerm) columns[col], null));
				}
			}

			this.tailStartsAt = tailStartsAt;
			if (tailVar != null) {
				subs.add(new Replacement(logFact.createVariableTerm(tailVar, ws.prova.RList.class), null));
			}

		} catch (SQLException ex) {
		}
	}

	/**
	 * Indicates whether there are more clauses.
	 *
	 * @return true if there are more clauses, false otherwise
	 * @throws ClauseSetException
	 */
	public boolean hasMoreClauses() throws ClauseSetException {
		if (exceptionWithHnadler) {
			exceptionWithHnadler = false;
			return true;
		}
		try {
			boolean hasNext = rs != null && rs.next();
			if (hasNext) {
				int i = 0;
				if (tailStartsAt < ncols) {
					// There was a tail variable to collect unspecified fields
					String extra = "[";
					ArrayList tailVars = new ArrayList();
					int ntail = 0;
					for (int col = 0; col < ncols; col++) {
						if (col >= tailStartsAt) {
							extra += "[" + rs.getMetaData().getColumnName(col + 1) + ",%" + (ntail++) + "]";
							if (col < ncols - 1) {
								extra += ',';
							}
							tailVars.add(logFact.createConstantTerm(rs.getObject(col + 1)));
							continue;
						}
						if (columns[col] == null) {
							// This column was a constant, not a variable
							continue;
						}
						((Replacement) subs.get(i++)).replacement = logFact.createConstantTerm(rs.getObject(col + 1));
					}
					extra += ']';
					tail = RListUtils.rlist(extra, tailVars.toArray());
					((Replacement) subs.get(i)).replacement = tail;
				} else {
					// No tail variable
					for (int col = 0; col < ncols; col++) {
						if (columns[col] == null) {
							// This column was a constant, not a variable
							continue;
						}
						((Replacement) subs.get(i++)).replacement = logFact.createConstantTerm(rs.getObject(col + 1));
					}
				}
				return true;
			} else {
				nextFact = null;
//				close();
				return false;
			}
		} catch (SQLException x) {
			nextFact = null;
			throw new ClauseSetException("Exception building facts from records");
		}
	}

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

	/**
	 * Close the iterator and release all resources.
	 */
	public void close() {
		try {
			if (rs != null && closeConnection) {
				rs.close();
				if (LOG_SQL.isDebugEnabled()) LOG_SQL.debug("JDBC connection released by SQLClauseIterator " + this);
			}
		} catch (SQLException x) {
			LOG_SQL.error("Exception closing result set in SQLExtendedClauseIterator", x);
		}
		try {
			if (stmt != null)
				stmt.close();
		} catch (Exception e) {
			LOG_SQL.error("Exception closing SQL Statement in SQLExtendedClauseIterator", e);
		}
		try {
			if (connection != null && closeConnection) connection.close();
		} catch (SQLException x) {
			LOG_SQL.error("Exception closing connection in SQLExtendedClauseIterator", x);
		}
	}
}

