/*
 * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden
 * Distributed under the terms shown in the file COPYRIGHT
 * found in the root folder of this project or at
 * http://eng.tada.se/osprojects/COPYRIGHT.html
 */
package org.postgresql.pljava.jdbc;

import java.sql.SQLException;
import java.sql.Statement;
import java.sql.ResultSetMetaData;

import org.postgresql.pljava.internal.Portal;
import org.postgresql.pljava.internal.SPI;
import org.postgresql.pljava.internal.TupleTable;
import org.postgresql.pljava.internal.Tuple;
import org.postgresql.pljava.internal.TupleDesc;

/**
 * A Read-only ResultSet that provides direct access to a {@link
 * org.postgresql.pljava.internal.Portal Portal}. At present, only
 * forward positioning is implemented. Attempts to use reverse or
 * absolute positioning will fail.
 *
 * @author Thomas Hallgren
 */
public class SPIResultSet extends ResultSetBase
{
	private final SPIStatement m_statement;
	private final Portal    m_portal;
	private final TupleDesc m_tupleDesc;
	private final int       m_maxRows;

	private Tuple m_currentRow;
	private Tuple m_nextRow;

	private TupleTable m_table;
	private int m_tableRow;

	SPIResultSet(SPIStatement statement, Portal portal, int maxRows)
	throws SQLException
	{
		super(statement.getFetchSize());
		m_statement = statement;
		m_portal = portal;
		m_maxRows = maxRows;
		m_tupleDesc = portal.getTupleDesc();
		m_tableRow = -1;
	}

	public int getFetchDirection()
	throws SQLException
	{
		return FETCH_FORWARD;
	}

	public void close()
	throws SQLException
	{
		if(m_portal.isValid())
		{
			m_portal.close();
			m_statement.resultSetClosed(this);
			m_table      = null;
			m_tableRow   = -1;
			m_currentRow = null;
			m_nextRow    = null;
			super.close();
		}
	}

	public boolean isLast() throws SQLException
	{
		return m_currentRow != null && this.peekNext() == null;
	}

	public boolean next()
	throws SQLException
	{
		m_currentRow = this.peekNext();
		m_nextRow = null;
		boolean result = (m_currentRow != null);
		this.setRow(result ? this.getRow() + 1 : -1);
		return result;
	}

	public String getCursorName()
	throws SQLException
	{
		return this.getPortal().getName();
	}

	public int findColumn(String columnName)
	throws SQLException
	{
		return m_tupleDesc.getColumnIndex(columnName);
	}

	public Statement getStatement()
	throws SQLException
	{
		return m_statement;
	}

	protected final Portal getPortal()
	throws SQLException
	{
		if(!m_portal.isValid())
			throw new SQLException("ResultSet is closed");
		return m_portal;
	}

	protected final TupleTable getTupleTable()
	throws SQLException
	{
		if(m_table == null)
		{
			Portal portal = this.getPortal();
			if(portal.isAtEnd())
				return null;

			int mx;
			int fetchSize = this.getFetchSize();
			if(m_maxRows > 0)
			{
				mx = m_maxRows - portal.getPortalPos();
				if(mx <= 0)
					return null;
				if(mx > fetchSize)
					mx = fetchSize;
			}
			else
				mx = fetchSize;

			try
			{
				int result = portal.fetch(true, mx);
				if(result > 0)
					m_table = SPI.getTupTable(m_tupleDesc);
				m_tableRow = -1;
			}
			finally
			{
				SPI.freeTupTable();
			}
		}
		return m_table;
	}

	protected final Tuple getCurrentRow()
	throws SQLException
	{
		if(m_currentRow == null)
			throw new SQLException("ResultSet is not positioned on a valid row");
		return m_currentRow;
	}

	protected final Tuple peekNext()
	throws SQLException
	{
		if(m_nextRow != null)
			return m_nextRow;

		TupleTable table = this.getTupleTable();
		if(table == null)
			return null;

		if(m_tableRow >= table.getCount() - 1)
		{
			// Current table is exhaused, get the next
			// one.
			//
			m_table = null;
			table = this.getTupleTable();
			if(table == null)
				return null;
		}
		m_nextRow = table.getSlot(++m_tableRow);
		return m_nextRow;
	}

	protected Object getObjectValue(int columnIndex)
	throws SQLException
	{
		return this.getCurrentRow().getObject(m_tupleDesc, columnIndex);
	}

	public ResultSetMetaData getMetaData()
	throws SQLException
	{
		return new SPIResultSetMetaData(m_tupleDesc);
	}
}
