package org.maachang.mimdb.core.impl ;

import java.io.OutputStream;

import org.maachang.mimdb.core.MimdbSearchElement;
import org.maachang.mimdb.core.util.ObjectBinary;
import org.maachang.mimdb.core.util.ObjectList;

/**
 * 判別ブロックオブジェクト.
 * 
 * @version 2013/10/27
 * @author masahito suzuki
 * @since MasterInMemDB 1.00
 */
public final class WhereBlock {
    
    private static final int ADD_LEN = 8 ;
    private WhereBlock top = null ;
    private Object[] list = new Object[ ADD_LEN ] ;
    private int length = 0 ;
    
    /** コンストラクタ. **/
    public WhereBlock() {}
    
    /**
     * オブジェクト追加.
     * @param n 対象のオブジェクトを設定します.
     * @return ブロックオブジェクトが返却されます.
     */
    public WhereBlock add( final Object n ) {
        if( length + 1 >= list.length ) {
            Object[] tmp = new Object[ length + ADD_LEN ] ;
            System.arraycopy( list,0,tmp,0,length ) ;
            list = tmp ;
        }
        list[ length ++ ] = n ;
        // 追加対象のオブジェクトが別のブロックオブジェクトの場合.
        if( n instanceof WhereBlock ) {
            ((WhereBlock)n).top = this ;
            return (WhereBlock)n ;
        }
        return this ;
    }
    
    /**
     * 上のオブジェクトを取得.
     * @return WhereBlock 上のオブジェクトが返却されます.
     */
    public WhereBlock getTop() {
        return top ;
    }
    
    /**
     * このブロック内に設定されたオブジェクトを取得.
     * @param no 対象の項番を設定します.
     * @return Object オブジェクトが返却されます.
     */
    public Object get( final int no ) {
        return list[ no ] ;
    }
    
    /**
     * このブロック内に設定されたオブジェクト数を取得.
     * @return int 設定数が返却されます.
     */
    public int size() {
        return length ;
    }
    
    /**
     * 文字列表示.
     * @return String 文字列として返却されます.
     */
    public String toString() {
        return toString( true ) ;
    }
    
    /**
     * 文字列表示.
     * @return String 文字列として返却されます.
     */
    public String toString( final boolean mode ) {
        StringBuilder buf = new StringBuilder() ;
        if( mode ) {
            toString( "",buf ) ;
        }
        else {
            sql( buf ) ;
        }
        return buf.toString() ;
    }
    
    /** 文字列表示. **/
    protected final void toString( final String shift,final StringBuilder buf ) {
        Object n ;
        for( int i = 0 ; i < length ; i ++ ) {
            n = list[ i ] ;
            if( n instanceof WhereBlock ) {
                ((WhereBlock)n).toString( "  " + shift,buf ) ;
            }
            else {
                buf.append( shift ).append( n ).append( "\n" ) ;
            }
        }
    }
    
    /** sql文字列表示. **/
    protected final void sql( final StringBuilder buf ) {
        Object n ;
        for( int i = 0 ; i < length ; i ++ ) {
            n = list[ i ] ;
            if( n instanceof WhereBlock ) {
                ((WhereBlock)n).sql( buf ) ;
            }
            else {
                buf.append( " " ).append( n ) ;
            }
        }
    }
    
    /**
     * バイナリ変換.
     * @param out 対象のOutputStreamを設定します.
     * @exception Exception 例外.
     */
    public void getOutput( final OutputStream out )
        throws Exception {
        Object n ;
        ObjectBinary.encode( out,length ) ;
        for( int i = 0 ; i < length ; i ++ ) {
            n = list[ i ] ;
            if( n instanceof WhereBlock ) {
                ObjectBinary.encode( out,true ) ;
                ( ( WhereBlock )n ).getOutput( out ) ;
            }
            else {
                ObjectBinary.encode( out,false ) ;
                ( ( MimdbSearchElement )n ).getOutput( out ) ;
            }
        }
    }
    
    /**
     * バイナリをオブジェクトに変換.
     * @param p オフセット値を保持するint[1]がセットされます.
     * @param list PreparedなMimdbSearchElementを格納するリストオブジェクトを設定します.
     * @parma b 対象のバイナリを設定します.
     * @param len 対象の長さを設定します.
     * @exception Exception 例外.
     */
    public void toObject( int[] p,ObjectList<MimdbSearchElement> lst,byte[] b,int len )
        throws Exception {
        // リスト全体の長さを取得.
        int listLength = (Integer)ObjectBinary.decodeBinary( p,b,len ) ;
        WhereBlock w ;
        SqlSearchElement e ;
        for( int i = 0 ; i < listLength ; i ++ ) {
            // WhereBlock.
            if( (Boolean)ObjectBinary.decodeBinary( p,b,len ) ) {
                w = new WhereBlock() ;
                w.toObject( p,lst,b,len ) ;
                add( w ) ;
            }
            // MimdbSearchElement.
            else {
                e = new SqlSearchElement( -1 ) ;
                e.toObject( p,b,len ) ;
                if( e.isPrepared() ) {
                    lst.add( e ) ;
                }
                add( e ) ;
            }
        }
    }
    
}

