package mit.sql ;

public class Blob
	implements	sql.Blob
{
	private static final String GETBYTES = "getBytes" ;
	private static final String POSITION = "position" ;
	private static final Long INVALIDPOSITION = new Long( -1 ) ;

	private static final sql.TableException LOCALBLOBGETBYTESFAILED
		= new sql.TableException
			( sql.TableException.BLOBGETBYTESFAILED
			) ;

	private static final sql.TableException LOCALBLOBPOSITIONFAILED
		= new sql.TableException
			( sql.TableException.BLOBPOSITIONFAILED
			) ;

	private mit.sql.Rows rows = null ;
	private int row = 0 ;
	private int col = 0 ;
	private Long pos = INVALIDPOSITION ;
	private Integer len = new Integer( 0 ) ;
	private byte[] pattern = null ;
	private Long start = INVALIDPOSITION ;

	private long length = -1 ;
	public long length() { return this.length ; }

	protected Blob
		( mit.sql.Rows rows
		, long length
		, int row
		, int col
		)
	{
		this.rows = rows ;
		this.row = row ;
		this.col = col ;
		this.length = length ;
	}

	public byte[] getBytes
		( Long pos
		, Integer len
		)
		throws Exception
	{
		this.pos = pos ;
		this.len = len ;
		mit.sql.Connection connection
			= ( mit.sql.Connection )
				rows.table.getConnection() ;
		byte[] bytes = null ;
		if( connection.hasClient() )
		{
			bytes = getBytesRemotely() ;
		}
		else
		{
			bytes = getBytesLocally() ;
		}
		return bytes ;
	}

	private byte[] getBytesRemotely()
		throws Exception
	{
		Object[] args = new Object[ 2 ] ;
		args[ 0 ] = this.pos ;
		args[ 1 ] = this.len ;
		Class[] argstypes = new Class[ 2 ] ;
		argstypes[ 0 ] = args[ 0 ].getClass() ;
		argstypes[ 1 ] = args[ 1 ].getClass() ;
		mit.sql.Connection connection
			= ( mit.sql.Connection )
				rows.table.getConnection() ;
		mit.rmi.Client client = connection.getClient() ;
		return ( byte[] ) client.invoke( this , GETBYTES , argstypes , args ) ;
	}

	public byte[] getBytesLocally()
		throws Exception
	{
		java.sql.ResultSet rs = null ;
		try
		{
			rs = rows.getResultSet() ;
			int rsType = rs.getType() ;
			if( rs.TYPE_FORWARD_ONLY == rsType )
			{
				if( rs.next() )
				{
					return getBytesFromResultSet( rs ) ;
				}
			}
			else if( rs.TYPE_SCROLL_INSENSITIVE == rsType )
			{
				rs.setFetchDirection( rs.FETCH_FORWARD ) ;
				if( rs.first() )
				{
					return getBytesFromResultSet( rs ) ;
				}
			}
			else if( rs.TYPE_SCROLL_SENSITIVE == rsType )
			{
				rs.setFetchDirection( rs.FETCH_FORWARD ) ;
				boolean isRowAvailable = rs.first() ;
				if( rs.first() )
				{
					return getBytesFromResultSet( rs ) ;
				}
			}
			java.sql.Statement statement = rs.getStatement() ;
			rs.close() ;
			statement.close() ;
		}
		catch( Exception ex )
		{
			if( null != rs )
			{
				rs.close() ;
			}
			String message = LOCALBLOBGETBYTESFAILED.getMessage() ;
			message += ex.getMessage() ;
			throw new sql.TableException( message ) ;
		}
		return null ;
	}

	private byte[] getBytesFromResultSet
		( java.sql.ResultSet rs 
		)
		throws Exception
	{
		int cursor = 0 ;
		boolean isRowAvailable = true ;
		while( isRowAvailable )
		{
			if( cursor == this.row )
			{
				java.sql.Blob blob = ( java.sql.Blob ) rs.getObject( this.col + 1 ) ;
				return blob.getBytes( this.pos.longValue() , this.len.intValue() ) ;
			}
			cursor ++ ;
			isRowAvailable = rs.next() ;
		}
		return null ;
	}

	public Long position
		( byte[] pattern
		, Long start
		)
		throws Exception
	{
		this.pattern = pattern ;
		this.start = start ;

		mit.sql.Connection connection
			= ( mit.sql.Connection )
				rows.table.getConnection() ;
		Long thePosition = INVALIDPOSITION ;
		if( connection.hasClient() )
		{
			thePosition = positionRemotely() ;
		}
		else
		{
			thePosition = positionLocally() ;
		}
		return thePosition ;
	}

	private Long positionRemotely()
		throws Exception
	{
		Object[] args = new Object[ 2 ] ;
		args[ 0 ] = this.pattern ;
		args[ 1 ] = this.start ;
		Class[] argstypes = new Class[ 2 ] ;
		argstypes[ 0 ] = args[ 0 ].getClass() ;
		argstypes[ 1 ] = args[ 1 ].getClass() ;
		mit.sql.Connection connection
			= ( mit.sql.Connection )
				rows.table.getConnection() ;
		mit.rmi.Client client = connection.getClient() ;
		return ( Long ) client.invoke( this , POSITION , argstypes , args ) ;
	}

	private Long positionLocally()
		throws Exception
	{
		java.sql.ResultSet rs = null ;
		try
		{
			rs = rows.getResultSet() ;
			int rsType = rs.getType() ;
			if( rs.TYPE_FORWARD_ONLY == rsType )
			{
				if( rs.next() )
				{
					return positionFromResultSet( rs ) ;
				}
			}
			else if( rs.TYPE_SCROLL_INSENSITIVE == rsType )
			{
				rs.setFetchDirection( rs.FETCH_FORWARD ) ;
				if( rs.first() )
				{
					return positionFromResultSet( rs ) ;
				}
			}
			else if( rs.TYPE_SCROLL_SENSITIVE == rsType )
			{
				rs.setFetchDirection( rs.FETCH_FORWARD ) ;
				boolean isRowAvailable = rs.first() ;
				if( rs.first() )
				{
					return positionFromResultSet( rs ) ;
				}
			}
			java.sql.Statement statement = rs.getStatement() ;
			rs.close() ;
			statement.close() ;
		}
		catch( Exception ex )
		{
			if( null != rs )
			{
				rs.close() ;
			}
			String message = LOCALBLOBPOSITIONFAILED.getMessage() ;
			message += ex.getMessage() ;
			throw new sql.TableException( message ) ;
		}
		return INVALIDPOSITION ;
	}

	private Long positionFromResultSet
		( java.sql.ResultSet rs 
		)
		throws Exception
	{
		int cursor = 0 ;
		boolean isRowAvailable = true ;
		while( isRowAvailable )
		{
			if( cursor == this.row )
			{
				java.sql.Blob blob = ( java.sql.Blob ) rs.getObject( this.col + 1 ) ;
				return new Long( blob.position( this.pattern , this.start.longValue() ) ) ;
			}
			cursor ++ ;
			isRowAvailable = rs.next() ;
		}
		return INVALIDPOSITION ;
	}

}
