package mit.sql ;

public class Rows
	implements	sql.Rows
{
	protected static int MAXNUMBEROFROWS = 1000 ;
	private static final Object[] NOARGS = new Object[ 0 ] ;
	private static final Class[] NOARGSTYPES = new Class[ 0 ] ;
	private static final String GETROWS = "getRows" ;

	private static final sql.TableException LOCALGETROWSFAILED
		= new sql.TableException
			( sql.TableException.GETROWSFAILED
			) ;

	private static final sql.TableException LOCALGETCOLUMNFAILED
		= new sql.TableException
			( sql.TableException.GETCOLUMNFAILED
			) ;

	private String catalog = null ;
	private String schemaPattern = null ;
	private String procedureNamePattern = null ;
	private String columnNamePattern = null ;
	private String procedureName = null ;
	private String tableNamePattern = null ;
	private String[] tableTypes = null ;

	protected mit.sql.Table table = null ;
	private String sql = null ;
	private Object[] objs = null ;
	private int[] targetSqlTypes = null ;
	private int[] scales = null ;

	private Object[] rows = null ;
	private int index = 0 ;
	private int offset = 0 ;

	private sql.RowsMetaData rowsMetaData = null ;
	public sql.RowsMetaData getRowsMetaData()
	{
		return this.rowsMetaData ;
	}

	public String[] getColumnNames()
		throws Exception
	{
		String[] columnNames
			= new String[ this.rowsMetaData.getColumnCount() ] ;
		for( int i = 0 ; columnNames.length != i ; i ++ )
		{
			sql.ColumnMetaData colMetaData
				= this.rowsMetaData.getColumnsMetaData()[ i ] ;
			columnNames[ i ]
				= colMetaData.getColumnName() ;
		}
		return columnNames ;

	}

	public int getColumn( String columnName )
		throws Exception
	{
		String[] columnNames = getColumnNames() ;
		for( int i = 0 ; columnNames.length != i ; i ++ )
		{
			if( columnName.equals( columnNames[ i ] ) )
			{
				return i ;
			}
		}
		throw LOCALGETCOLUMNFAILED ;
	}

	public boolean hasMoreRows()
		throws Exception
	{
		if( null != rows )
		{
			if( index < rows.length )
			{
				return true ;
			}
			else
			{
				this.offset += index ;
				index = 0 ;
				rows = null ;
				if( null != table )
				{
					getRows() ;
					return( null != this.rows ) ;
				}
			}
		}
		return false ;
	}

	public Object[] nextRow()
		throws Exception
	{
		if( null != rows )
		{
			return ( Object[] ) rows[ index ++ ] ;
		}
		throw LOCALGETROWSFAILED ;
	}

	protected Rows
		( sql.RowsMetaData rowsMetaData
		, Object[] rows
		)
		throws Exception
	{
		this.rowsMetaData = rowsMetaData ;
		this.rows = rows ;
	}

	protected Rows
		( mit.sql.Table table
		, String catalog
		, String schemaPattern
		, String tableNamePattern
		, String[] tableTypes
		)
		throws Exception
	{
		this.table = table ;
		this.catalog = catalog ;
		this.schemaPattern = schemaPattern ;
		this.tableNamePattern = tableNamePattern ;
		this.tableTypes = tableTypes ;

		getRows() ;
	}

	protected Rows
		( mit.sql.Table table
		, String catalog
		, String schemaPattern
		, String procedureNamePattern
		)
		throws Exception
	{
		this.table = table ;
		this.catalog = catalog ;
		this.schemaPattern = schemaPattern ;
		this.procedureNamePattern = procedureNamePattern ;

		getRows() ;
	}

	protected Rows
		( mit.sql.Table table
		, String catalog
		, String schemaPattern
		, String procedureName
		, String columnNamePattern
		)
		throws Exception
	{
		this.table = table ;
		this.catalog = catalog ;
		this.schemaPattern = schemaPattern ;
		this.procedureName = procedureName ;
		this.columnNamePattern = columnNamePattern ;

		getRows() ;
	}

	protected Rows
		( mit.sql.Table table
		, String sql
		, Object[] objs
		, int[] targetSqlTypes
		, int[] scales
		)
		throws Exception
	{
		this.table = table ;
		this.sql = sql ;
		this.objs = objs ;
		this.scales = scales ;

		getRows() ;
	}

	public void getRows()
		throws Exception
	{
		mit.sql.Connection connection
			= ( mit.sql.Connection )
				( ( mit.sql.Table ) table ).getConnection() ;
		if( connection.hasClient() )
		{
			getRowsRemotely() ;
		}
		else
		{
			getRowsLocally() ;
		}
	}

	private void getRowsRemotely()
		throws Exception
	{
		mit.sql.Connection connection
			= ( mit.sql.Connection )
				( ( mit.sql.Table ) table ).getConnection() ;
		mit.rmi.Client client = connection.getClient() ;
		client.invoke( this , GETROWS , NOARGSTYPES , NOARGS ) ;
	}

	public void getRowsLocally()
		throws Exception
	{
		java.sql.ResultSet rs = getResultSet() ;
		java.sql.ResultSetMetaData rsMeta = rs.getMetaData() ;
		this.rowsMetaData = new mit.sql.RowsMetaData( rsMeta ) ;
		this.rows = createRows( rs ) ;
		java.sql.Statement statement = rs.getStatement() ;
		rs.close() ;
		statement.close() ;
	}

	protected java.sql.ResultSet getResultSet()
		throws Exception
	{
		if
			(  ( null != tableNamePattern )
			|| ( null != tableTypes )
			)
		{
			return getTables() ;
		}
		else if( null != this.sql )
		{
			return getQuery() ;
		}
		else if
			(  ( null != this.procedureName )
			|| ( null != this.columnNamePattern )
			)
		{
			return getProcedureColumns() ;
		}
		else
		{
			return getProcedures() ;
		}
	}

	private java.sql.Connection getConnection()
		throws Exception
	{
		sql.Connection conn =
			( ( mit.sql.Table ) this.table
			).getConnection() ;

		java.sql.Connection connection =
			( ( mit.sql.Connection ) conn
			).getConnection() ;
		return connection ;
	}

	private java.sql.ResultSet getTables()
		throws Exception
	{
		java.sql.DatabaseMetaData meta
			= getConnection().getMetaData() ;

		return meta.getTables
				( this.catalog
				, this.schemaPattern
				, this.tableNamePattern
				, this.tableTypes
				) ;
	}

	private java.sql.ResultSet getQuery()
		throws Exception
	{
		java.sql.Connection connection
			= getConnection() ;
		java.sql.ResultSet rs = null ;
		if( null == objs )
		{
			java.sql.Statement statement
				= connection.createStatement() ;
			rs = statement.executeQuery( sql ) ;
		}
		else
		{
			java.sql.PreparedStatement statement
				= connection.prepareStatement( sql ) ;
			for( int i = 0 ; objs.length != i ; i ++ )
			{
				if( null != this.scales )
				{
					statement.setObject
						( i + 1
						, objs[ i ]
						, targetSqlTypes[ i ]
						, scales[ i ]
						) ;
				}
				else if ( null != targetSqlTypes )
				{
					statement.setObject
						( i + 1
						, objs[ i ]
						, targetSqlTypes[ i ]
						) ;
				}
				else
				{
					statement.setObject
						( i + 1
						, objs[ i ]
						) ;
				}
			}
			rs = statement.executeQuery() ;
		}
		return rs ;
	}

	private java.sql.ResultSet getProcedureColumns()
		throws Exception
	{
		java.sql.DatabaseMetaData meta
			= getConnection().getMetaData() ;

		return meta.getProcedureColumns
				( this.catalog
				, this.schemaPattern
				, this.procedureName
				, this.columnNamePattern
				) ;
	}

	private java.sql.ResultSet getProcedures()
		throws Exception
	{
		java.sql.DatabaseMetaData meta
			= getConnection().getMetaData() ;

		return meta.getProcedures
				( this.catalog
				, this.schemaPattern
				, this.procedureNamePattern
				) ;
	}

	private Object[] createRows( java.sql.ResultSet rs )
		throws Exception
	{
		try
		{
			int rsType = rs.getType() ;
			if( rs.TYPE_FORWARD_ONLY == rsType )
			{
				if( rs.next() )
				{
					return createCells( rs ) ;
				}
			}
			else if( rs.TYPE_SCROLL_INSENSITIVE == rsType )
			{
				rs.setFetchDirection( rs.FETCH_FORWARD ) ;
				if( rs.first() )
				{
					return createCells( rs ) ;
				}
			}
			else if( rs.TYPE_SCROLL_SENSITIVE == rsType )
			{
				rs.setFetchDirection( rs.FETCH_FORWARD ) ;
				boolean isRowAvailable = rs.first() ;
				if( rs.first() )
				{
					return createCells( rs ) ;
				}
			}
		}
		catch( Exception ex )
		{
			String message = LOCALGETROWSFAILED.getMessage() ;
			message += ex.getMessage() ;
			throw new sql.TableException( message ) ;
		}
		return null ;
	}

	private Object[] createCells( java.sql.ResultSet rs )
		throws Exception
	{
		Object[] rows = new Object[ MAXNUMBEROFROWS ] ;
		int cursor = 0 ;
		int rowsAdded = 0 ;
		boolean isRowAvailable = true ;
		while( isRowAvailable )
		{
			if( cursor >= this.offset )
			{
				Object[] columns
					= new Object
						[ this.rowsMetaData.getColumnCount()
						] ;
				for( int i = 0 ; columns.length != i ; i ++ )
				{
					columns[ i ] = createCell( rs.getObject( i + 1 ) , rowsAdded , i ) ;
				}
				rows[ rowsAdded ] = columns ;
				rowsAdded ++ ;

			}
			if( MAXNUMBEROFROWS == rowsAdded )
			{
				break ;
			}
			cursor ++ ;
			isRowAvailable = rs.next() ;
		}
		if( 0 == rowsAdded )
		{
			return null ;
		}
		if( MAXNUMBEROFROWS > rowsAdded )
		{
			Object[] newRows = new Object[ rowsAdded ] ;
			for( int j = 0 ; rowsAdded != j ; j ++ )
			{
				newRows[ j ] = rows[ j ] ;
			}
			rows = newRows ;
		}
		return rows ;
	}

	private Object createCell
		( Object obj
		, int row
		, int col
		)
		throws Exception
	{
		Object cell = obj ;
		if( obj instanceof java.sql.Blob )
		{
			cell = makeBlob( ( java.sql.Blob ) obj , row , col ) ;
		}
		else if( obj instanceof java.sql.Clob )
		{
			cell = makeClob( ( java.sql.Clob ) obj , row , col ) ;
		}
		return obj ;
	}

	private sql.Blob makeBlob
		( java.sql.Blob blob
		, int row
		, int col
		)
		throws Exception
	{
		return new mit.sql.Blob
			( ( mit.sql.Rows ) this.clone()
			, blob.length()
			, row
			, col
			) ;
	}

	private sql.Clob makeClob
		( java.sql.Clob clob
		, int row
		, int col
		)
		throws Exception
	{
		return new mit.sql.Clob
			( ( mit.sql.Rows ) this.clone()
			, clob.length()
			, row
			, col
			) ;
	}

}