package org.maachang.mimdb.core;

import org.maachang.mimdb.MimdbException;
import org.maachang.mimdb.server.ConnectionDefine;


/**
 * Remote用検索結果情報.
 * 
 * @version 2014/01/20
 * @author masahito suzuki
 * @since MasterInMemDB 1.02
 */
final class RemoteResultImpl implements MimdbResult {
    
    /** リモートコネクションオブジェクト. **/
    protected RemoteConnection connection = null ;
    
    /** コンパイルオブジェクト. **/
    protected QueryCompileInfo compile = null ;
    
    /** 取得ポジション. **/
    protected int position = -1 ;
    
    /** フェッチサイズ. **/
    protected int fetchSize = -1 ;
    
    /** ResultSetId. **/
    protected int resultId = -1 ;
    
    /** データ数. **/
    protected int length = -1 ;
    
    /** 最大データ数. **/
    protected int maxLength = -1 ;
    
    /** 現在読み込み中の行番号. **/
    protected int nowRow = -1 ;
    
    /** データ. **/
    protected Object[][] values = null ;
    
    /** データ行数. **/
    protected int valueLength = 0 ;
    
    /** 行データオブジェクト. **/
    protected final RemoteResultRow row = new RemoteResultRow() ;
    
    /** ResultMeta **/
    protected RemoteMetaDataImpl meta = null ;
    
    /** 情報取得配列. **/
    protected final int[] out = new int[ 1 ] ;
    
    /** コンストラクタ. **/
    protected RemoteResultImpl() {}
    
    /**
     * コンストラクタ.
     * @param conn 対象のリモートコネクションオブジェクトを設定します.
     * @param cmp 対象のコンパイル済みオブジェクトを設定します.
     * @param mt 対象のメタデータオブジェクトを設定します.
     * @param fetch フェッチ初期値を設定します.
     * @param resId ResultIdを設定します
     * @param len 結果データ長を設定します.
     * @param max 結果最大データ長を設定します.
     */
    public RemoteResultImpl( RemoteConnection conn,QueryCompileInfo cmp,
        RemoteMetaDataImpl mt,int fetch,int resId,int len,int max ) {
        create( conn,cmp,mt,fetch,resId,len,max ) ;
    }
    
    /**
     * デストラクタ.
     */
    protected void finalize() throws Exception {
        clear() ;
    }
    
    /**
     * 情報生成.
     * @param conn 対象のリモートコネクションオブジェクトを設定します.
     * @param cmp 対象のコンパイル済みオブジェクトを設定します.
     * @param mt 対象のメタデータオブジェクトを設定します.
     * @param fetch フェッチ初期値を設定します.
     * @param resId ResultIdを設定します
     * @param len 結果データ長を設定します.
     * @param max 結果最大データ長を設定します.
     */
    public void create( RemoteConnection conn,QueryCompileInfo cmp,
        RemoteMetaDataImpl mt,int fetch,int resId,int len,int max ) {
        resultId = resId ;
        connection = conn ;
        compile = cmp ;
        position = -1 ;
        length = len ;
        maxLength = max ;
        
        fetchSize = fetch ;
        values = null ;
        nowRow = -1 ;
        
        if( mt == null ) {
            mt = new RemoteMetaDataImpl( conn,cmp,true ) ;
        }
        meta = mt ;
        row.create( this ) ;
    }
    
    /**
     * 情報クリア.
     */
    public void clear() {
        if( connection != null ) {
            try {
                connection.client.sendClose(
                    ConnectionDefine.CLOSE_RESULT_SET,resultId ) ;
            } catch( Exception e ) {
            }
            connection = null ;
        }
        resultId = -1 ;
        compile = null ;
        values = null ;
        if( meta != null && meta.isClearFlag ) {
            meta.clear() ;
        }
        meta = null ;
        row.clear() ;
    }
    
    /** オブジェクトがクリアされているかチェック. **/
    protected final boolean isClearData() {
        return connection == null ;
    }
    
    /** チェック処理. **/
    protected final void check() {
        if( connection == null || connection.isClose() ) {
            throw new MimdbException( "オブジェクトは既にクローズします" ) ;
        }
    }
    
    /**
     * DB更新IDの取得.
     * @return long DB更新IDが返却されます.
     */
    public long getDbId() {
        check() ;
        return compile.getDbId() ;
    }
    
    /**
     * メタデータを取得.
     * @return MimdbMetaData メタデータが返却されます.
     */
    public MimdbMetaData getMetaData() {
        check() ;
        return meta ;
    }
    
    /**
     * テーブルオブジェクトを取得.
     * @return BaseTable テーブルオブジェクトが返却されます.
     */
    public BaseTable getTable() {
        check() ;
        try {
            return connection.client.getTable( compile.name ) ;
        } catch( RuntimeException r ) {
            throw r ;
        } catch( Exception e ) {
            throw new MimdbException( e ) ;
        }
    }
    
    /**
     * 結果データ数を取得.
     * @return int 結果データ数が返却されます.
     */
    public int length() {
        check() ;
        return length ;
    }
    
    /**
     * 最大データ長を取得.
     * @return int 最大データ長が返却されます.
     */
    public int maxLength() {
        check() ;
        return maxLength ;
    }
    
    /**
     * 最初の行に移動.
     * @return boolean [true]の場合、存在します.
     * @exception Exception 例外.
     */
    public boolean first() throws Exception {
        nowRow = -1 ;
        return length > 0 ;
    }
    
    /**
     * 最後の行に移動.
     * @return boolean [true]の場合、存在します.
     * @exception Exception 例外.
     */
    public boolean last() throws Exception {
        nowRow = length ;
        return length > 0 ;
    }
    
    /**
     * 次の情報が存在するかチェック.
     * @return boolean [true]の場合、存在します.
     */
    public boolean next() {
        nowRow ++ ;
        return nowRow >= 0 && length > nowRow ;
    }
    
    /**
     * 指定行に移動.
     * @param no 対象の行番号を設定します.
     * @return boolean [true]の場合、存在します.
     * @exception Exception 例外.
     */
    public boolean absolute( final int no ) throws Exception {
        nowRow = no ;
        return nowRow >= 0 && length > nowRow ;
    }
    
    /**
     * 現在の行番号を取得.
     * @return int 現在の行番号が返却されます.
     */
    public int getRow() {
        check() ;
        return nowRow ;
    }
    
    /**
     * フェッチサイズを設定.
     * ※この値は、サーバーモードでの接続のみ有効となります.
     * @param size フェッチサイズを設定します.
     * @return MimdbResult このオブジェクトが返却されます.
     */
    public MimdbResult setFetchSize( int size ) {
        check() ;
        if( size <= 0 ) {
            size = 1 ;
        }
        fetchSize = size ;
        return this ;
    }
    
    /**
     * フェッチサイズを取得.
     * ※この値は、サーバーモードでの接続のみ有効となります.
     * @return int フェッチサイズが返却されます.
     */
    public int getFetchSize() {
        check() ;
        return fetchSize ;
    }
    
    /**
     * 結果データの取得.
     * @return MimdbResultRow 結果行情報が返却されます.
     * @exception Exception 例外.
     */
    public MimdbResultRow get() throws Exception {
        if( readBuffer() ) {
            return row ;
        }
        throw new MimdbException( "取得範囲を超えています" ) ;
    }
    
    /**
     * 結果データの取得.
     * ※この条件では、カーソルは移動されません.
     * @param no 対象の行番号を設定します.
     * @return MimdbResultRow 結果行情報が返却されます.
     * @exception Exception 例外.
     */
    public MimdbResultRow get( final int no ) throws Exception {
        nowRow = no ;
        return get() ;
    }
    
    /** 結果情報を取得. **/
    protected final boolean readBuffer() throws Exception {
        if( nowRow < 0 ) {
            return false ;
        }
        
        final int end = position + valueLength ;
        // 情報が存在しないか、情報保持範囲を超えている場合.
        if( values == null || nowRow < position || nowRow >= end ) {
            
            // 情報を取得.
            Object[][] o = connection.client.resultSet( out,resultId,nowRow,fetchSize ) ;
            if( o == null ) {
                return false ;
            }
            
            valueLength = out[ 0 ] ;
            position = nowRow ;
            values = o ;
            
        }
        
        return true ;
    }
    
}

