package org.maachang.mimdb.core ;

import org.maachang.mimdb.MimdbException;

/**
 * Remote用ステートメント.
 * 
 * @version 2014/01/16
 * @author masahito suzuki
 * @since MasterInMemDB 1.02
 */
final class RemoteStatement implements MimdbStatement {
    
    /** リモートコネクションオブジェクト. **/
    protected RemoteConnection connection = null ;
    
    /** フェッチサイズ. **/
    protected int fetchSize = RemoteConnection.DEF_FETCH_SIZE ;
    
    /** オフセット、リミット値. **/
    protected int offset = -1 ;
    protected int limit = -1 ;
    
    /** データ受け取り条件. **/
    protected int[] out = new int[ 3 ] ;
    
    /** 結果オブジェクト. **/
    protected RemoteResultImpl result = null ;
    
    /** メタオブジェクト. **/
    protected final RemoteMetaDataImpl meta = new RemoteMetaDataImpl() ;
    
    
    
    /** コンストラクタ. **/
    protected RemoteStatement() {}
    
    /** コンストラクタ. **/
    protected RemoteStatement( RemoteConnection conn ) {
        open( conn ) ;
    }
    
    /** ファイナライズ. **/
    protected void finalize() throws Exception {
        close() ;
    }
    
    /** オープン. **/
    protected void open( RemoteConnection conn ) {
        connection = conn ;
    }
    
    /**
     * オブジェクトクローズ.
     */
    public void close() {
        connection = null ;
    }
    
    /**
     * オブジェクトがクローズしているか取得.
     * @return boolean [true]の場合、クローズしています.
     */
    public boolean isClose() {
        return connection == null || connection.isClose() ;
    }
    
    /** チェック処理. **/
    protected void check() {
        if( isClose() ) {
            throw new MimdbException( "オブジェクトはクローズしています" ) ;
        }
    }
    
    /**
     * 表示オフセット値を設定.
     * この条件により、表示位置を確定できます.
     * @param off 表示オフセット値を設定します.
     *            [-1]が設定された場合、表示幅は確定されません.
     * @return MimdbStatement このオブジェクトが返却されます.
     */
    public MimdbStatement setOffset( int off ) {
        check() ;
        this.offset = off ;
        return this ;
    }
    
    
    /**
     * 表示リミット値を設定.
     * この条件により、表示位置を確定できます.
     * @param limit 表示リミット値を設定します.
     *            [-1]が設定された場合、表示幅は確定されません.
     * @return MimdbStatement このオブジェクトが返却されます.
     */
    public MimdbStatement setLimit( int limit ) {
        check() ;
        this.limit = limit ;
        return this ;
    }
    
    /** オフセット、リミット値をクリア. **/
    protected void clearOffLimit() {
        offset = -1 ;
        limit = -1 ;
    }
    
    /**
     * フェッチサイズを設定.
     * ※この値は、サーバーモードでの接続のみ有効となります.
     * @param size フェッチサイズを設定します.
     * @return MimdbStatement このオブジェクトが返却されます.
     */
    public MimdbStatement setFetchSize( int size ) {
        check() ;
        fetchSize = size ;
        return this ;
    }
    
    /**
     * フェッチサイズを取得.
     * ※この値は、サーバーモードでの接続のみ有効となります.
     * @return int フェッチサイズが返却されます.
     */
    public int getFetchSize() {
        check() ;
        return fetchSize ;
    }
    
    /**
     * クエリー実行.
     * @param sql 対象のSQL文を設定します.
     * @return MimdbResult 実行結果が返却されます.
     * @exception Exception 例外.
     */
    public MimdbResult executeQuery( String sql )
        throws Exception {
        check() ;
        
        QueryCompileInfo compile = null ;
        
        // オブジェクトをコンパイル.
        try {
            compile = RemoteConnection.compile( connection,sql ) ;
        } catch( RuntimeException r ) {
            throw r ;
        } catch( Exception e ) {
            throw new MimdbException( e ) ;
        }
        
        // コンパイルオブジェクトがパラメータ付きの場合はエラー.
        if( compile.preparedParamsSize > 0 ) {
            throw new MimdbException( "パラメータ付きのSQL文は実行できません" ) ;
        }
        
        // オフセット、リミット値設定.
        int off,lmt ;
        off  = compile.defOffset ;
        lmt  = compile.defLimit ;
        if( offset >= 0 ) {
            off = offset ;
            offset = -1 ;
        }
        if( limit >= 0 ) {
            lmt = limit ;
            limit = -1 ;
        }
        
        // 取得パラメータ初期化.
        out[ 0 ] = -1 ;
        out[ 1 ] = -1 ;
        out[ 2 ] = -1 ;
        
        // ステートメント実行.
        connection.client.statement( out,compile,off,lmt ) ;
        
        // 処理結果を返却.
        return getResult(
            connection,compile,getMeta( connection,compile ),fetchSize,out[0],out[1],out[2] ) ;
    }
    
    /** RemoteResultImplを生成. **/
    private final RemoteResultImpl getResult( RemoteConnection conn,QueryCompileInfo cmp,
        RemoteMetaDataImpl mt,int fetch,int resId,int len,int max ) {
        
        if( connection.result.isClearData() ) {
            connection.result.create( conn,cmp,mt,fetch,resId,len,max ) ;
            return connection.result ;
        }
        else if( result == null ) {
            result = new RemoteResultImpl( conn,cmp,mt,fetch,resId,len,max ) ;
            return result ;
        }
        else if( result.isClearData() ) {
            result.create( conn,cmp,mt,fetch,resId,len,max ) ;
            return result ;
        }
        return new RemoteResultImpl( conn,cmp,mt,fetch,resId,len,max ) ;
    }
    
    /** RemoteMetaDataImplを生成. **/
    private final RemoteMetaDataImpl getMeta( RemoteConnection conn,QueryCompileInfo cmp ) {
        if( meta.isClearData() ) {
            meta.create( conn,cmp,true ) ;
            return meta ;
        }
        return new RemoteMetaDataImpl( conn,cmp,true ) ;
    }
}
