package org.maachang.mimdb.core.impl ;

import org.maachang.mimdb.core.MimdbMiddleSearch;
import org.maachang.mimdb.core.ResultArray;

/**
 * Flagsオブジェクトでの検索途中結果情報.
 * 
 * @version 2013/10/07
 * @author masahito suzuki
 * @since MasterInMemDB 1.00
 */
public final class FlagsMiddleSearch implements MimdbMiddleSearch {
    
    /** DbId. **/
    protected long dbId = -1L ;
    
    /** フラグオブジェクト. **/
    protected Flags noList = null ;
    
    /** データ格納数. **/
    protected int max = -1 ;
    
    /** 有効データ件数キャッシュ. **/
    protected int useSize = -1 ;
    
    /** コンストラクタ. **/
    public FlagsMiddleSearch() {}
    
    /**
     * コンストラクタ.
     * ※新規作成の場合に利用します.
     * @param id DB更新IDを設定します.
     * @param len データ最大格納数を設定します.
     */
    public FlagsMiddleSearch( final long id,final int len ) {
        create( id,len ) ;
    }
    
    /**
     * コンストラクタ.
     * ※新規作成の場合に利用します.
     * @param id DB更新IDを設定します.
     * @param len データ最大格納数を設定します.
     */
    public final void create( final long id,final int len ) {
        max = len ;
        dbId = id ;
        if( len > AbstractLargeFlags.TARGET_LENGTH ) {
            noList = new LargeFlagsBase( len ) ;
        }
        else {
            noList = new SmallFlagsBase( len ) ;
        }
    }
    
    /**
     * 情報生成.
     * ※ANDセットの場合に利用します.
     * @param id DB更新IDを設定します.
     * @param f 対象のフラグオブジェクトを設定します.
     */
    public final void create( final long id,final Flags f ) {
        useSize = -1 ;
        dbId = id ;
        if( f.isAnd() ) {
            noList = f ;
            if( f.max() > AbstractLargeFlags.TARGET_LENGTH ) {
                ((LargeFlagsAnd)f).create( f ) ;
            }
            else {
                ((SmallFlagsAnd)f).create( f ) ;
            }
        }
        else {
            if( f.max() > AbstractLargeFlags.TARGET_LENGTH ) {
                noList = new LargeFlagsAnd( f ) ;
            }
            else {
                noList = new SmallFlagsAnd( f ) ;
            }
        }
        max = noList.max() ;
    }
    
    /**
     * 情報クリア.
     */
    public final void clear() {
        dbId = -1L ;
        noList = null ;
        max = -1 ;
        useSize = -1 ;
    }
    
    /**
     * DbIdを取得.
     * @return long DbIdが返却されます.
     */
    public final long getDbId() {
        return dbId ;
    }
    
    /**
     * 行データをセット.
     * @param no 行番号を設定します.
     */
    public final void add( final int no ) {
        noList.add( no ) ;
    }
    
    /**
     * 行データをセット.
     * @param no 行番号群を設定します.
     */
    public final void addArray( final int[] no ) {
        noList.addArray( no ) ;
    }
    
    /**
     * 行データをOFF.
     * @param no 行番号を設定します.
     */
    public final void off( final int no ) {
        noList.set( no,0 ) ;
    }
    
    /**
     * 行データをOFF.
     * @param no 行番号群を設定します.
     */
    public final void offArray( final int[] no ) {
        noList.setArray( no,0 ) ;
    }
    
    /**
     * 行データを全てONにセット.
     */
    public final void all() {
        noList.all() ;
    }
    
    /**
     * 行データをマージOrセット.
     * ※この処理を行った場合、AND属性がOR属性に変更されます.
     * @param search マージ対象の検索データを設定します.
     */
    public final void or( final MimdbMiddleSearch search ) {
        
        // 現在の条件がAND条件の場合.
        if( noList.isAnd() ) {
            
            // 現在の属性をOR属性に変更.
            if( noList.max() > AbstractLargeFlags.TARGET_LENGTH ) {
                noList = new LargeFlagsBase( noList ) ;
            }
            else {
                noList = new SmallFlagsBase( noList ) ;
            }
        }
        
        // orマージ.
        noList.marge( ((FlagsMiddleSearch)search).noList ) ;
    }
    
    /**
     * 行データをマージAndセット.
     * ※この処理を行った場合、OR属性がAND属性に変更されます.
     * @param search マージ対象の検索データを設定します.
     */
    public final void and( final MimdbMiddleSearch search ) {
        
        // 現在の条件がOR条件の場合.
        if( !noList.isAnd() ) {
            
            // 現在の属性をAND属性に変更.
            if( noList.max() > AbstractLargeFlags.TARGET_LENGTH ) {
                noList = new LargeFlagsAnd( noList ) ;
            }
            else {
                noList = new SmallFlagsAnd( noList ) ;
            }
        }
        
        // orマージ.
        noList.marge( ((FlagsMiddleSearch)search).noList ) ;
    }
    
    /**
     * 行データオブジェクトを取得.
     * @param out 行データオブジェクトが返却されます.
     * @return int 有効情報数が返却されます.
     */
    public final int getResultArray( final ResultArray[] out ) {
        return noList.getResultArray( out ) ;
    }
    
    /**
     * 行データオブジェクトを取得.
     * @param out 行データオブジェクトが返却されます.
     * @param off 対象のオフセット値を設定します.
     * @return int 有効情報数が返却されます.
     */
    public final int getResultArray( final ResultArray[] out,final int off ) {
        return noList.getResultArray( out,off ) ;
    }
    
    /**
     * 件数キャッシュクリア.
     */
    public void clearCache() {
        useSize = -1 ;
    }
    
    /**
     * データ数を取得.
     * ※この処理は１度取得するとキャッシュされるので、クリアする場合は、
     *   clearCacheを呼び出す必要があります.
     * @return int データ数が返却されます.
     */
    public final int size() {
        if( useSize == -1 ) {
            useSize = noList.size() ;
        }
        return useSize ;
    }
    
    /**
     * このオブジェクトがAND属性かチェック.
     * @return boolean [true]の場合、AND属性です.
     */
    public final boolean isAnd() {
        return noList.isAnd() ;
    }
    
    /**
     * 文字情報出力.
     * @return String 文字列が返却されます.
     */
    public final String toString() {
        return new StringBuilder( noList.isAnd() ? "and" : "or" ).
            append( " size:" ).append( size() ).toString() ;
    }
}
