package org.maachang.mimdb.core.impl ;

import org.maachang.mimdb.MimdbException;


/**
 * Andフラグリスト.
 * 
 * @version 2013/10/27
 * @author masahito suzuki
 * @since MasterInMemDB 1.00
 */
public final class SmallFlagsAnd extends AbstractSmallFlags {
    
    /** And元フラグ. **/
    private int[][] src ;
    
    /**
     * コンストラクタ.
     */
    public SmallFlagsAnd() {
        
    }
    
    /**
     * コンストラクタ.
     * @param f And元のオブジェクトをセットします.
     *          渡されたオブジェクトは内部でクリアされます.
     */
    public SmallFlagsAnd( final Flags f ) {
        create( f ) ;
    }
    
    /** デストラクタ. **/
    protected void finalize() throws Exception {
        destroy() ;
    }
    
    /**
     * 情報生成.
     * @param f And元のオブジェクトをセットします.
     *          渡されたオブジェクトは内部でクリアされます.
     */
    public final void create( final Flags f ) {
        AbstractSmallFlags sf = (AbstractSmallFlags)f ;
        int[][] s = sf.flags ;
        int m = sf.max ;
        f.destroy() ;
        
        src = s ;
        max = m ;
        flags = new int[ src.length ][] ;
    }
    
    /**
     * 情報クリア.
     */
    public final void destroy() {
        super.destroy() ;
        src = null ;
    }
    
    /**
     * Andモード取得.
     * @return boolean [true]の場合、ANDモードです.
     */
    public final boolean isAnd() {
        return true ;
    }
    
    /**
     * 指定位置のフラグをON.
     * @param no 対象の項番を設定します.
     */
    public final void add( final int no ) {
        int[] ff ;
        final int n = no >> BLOCK_SHIFT ;
        final int nn = no & BLOCK_MASK ;
        
        // AND対象位置の条件が0の場合は処理しない.
        if( ( ff = src[ n ] ) != null &&
            ( ff[ nn >> BLOCK_INNER_SHIFT ] & ( 1 << ( nn & BLOCK_INNER_MASK ) ) ) != 0 ) {
            
            // ブロック位置条件を取得.
            if( ( ff = flags[ n ] ) == null ) {
                ff = new int[ BLOCK_INNER_SIZE ] ;
                flags[ n ] = ff ;
            }
            
            // フラグセット.
            ff[ nn >> BLOCK_INNER_SHIFT ] |= ( 1 << ( nn & BLOCK_INNER_MASK ) ) ;
            
        }
    }
    
    /**
     * 指定位置のフラグをON.
     * @param array 対象の項番群を設定します.
     */
    public final void addArray( final int[] array ) {
        int a,b,n,nn ;
        int[] ff ;
        b = -1 ;
        final int len = array.length ;
        for( int i = 0 ; i < len ; i ++ ) {
            
            a = array[ i ] ;
            n = a >> BLOCK_SHIFT ;
            nn = a & BLOCK_MASK ;
            
            // AND対象位置の条件が0の場合は処理しない.
            if( ( ff = src[ n ] ) != null &&
                ( ff[ nn >> BLOCK_INNER_SHIFT ] & ( 1 << ( nn & BLOCK_INNER_MASK ) ) ) != 0 ) {
                
                if( b != n ) {
                    // ブロック位置条件を取得.
                    if( ( ff = flags[ n ] ) == null ) {
                        ff = new int[ BLOCK_INNER_SIZE ] ;
                        flags[ n ] = ff ;
                    }
                    b = n ;
                }
                
                // フラグセット.
                ff[ nn >> BLOCK_INNER_SHIFT ] |= ( 1 << ( nn & BLOCK_INNER_MASK ) ) ;
                
            }
        }
    }
    
    /**
     * 指定位置のフラグを設定.
     * @param no 対象の項番を設定します.
     * @param f フラグ条件を設定します.
     *          [0]がOFF、[1]がONです.
     */
    public final void set( final int no,final int f ) {
        int[] ff ;
        final int n = no >> BLOCK_SHIFT ;
        final int nn = no & BLOCK_MASK ;
        
        // AND対象位置の条件が0の場合は処理しない.
        if( ( ff = src[ n ] ) != null &&
            ( ff[ nn >> BLOCK_INNER_SHIFT ] & ( 1 << ( nn & BLOCK_INNER_MASK ) ) ) != 0 ) {
            
            // ブロック位置条件を取得.
            if( ( ff = flags[ n ] ) == null ) {
                ff = new int[ BLOCK_INNER_SIZE ] ;
                flags[ n ] = ff ;
            }
            
            // フラグセット.
            ff[ nn >> BLOCK_INNER_SHIFT ] = ( ff[ nn >> BLOCK_INNER_SHIFT ] &
                ~( 1 << ( nn & BLOCK_INNER_MASK ) ) ) |
                ( ( f & 0x1 ) << ( nn & BLOCK_INNER_MASK ) ) ;
            
        }
    }
    
    /**
     * 指定位置のフラグを設定.
     * @param array 対象の項番群を設定します.
     * @param f フラグ条件を設定します.
     *          [0]がOFF、[1]がONです.
     */
    public final void setArray( final int[] array,final int f ) {
        int a,b,n,nn ;
        int[] ff ;
        b = -1 ;
        final int len = array.length ;
        for( int i = 0 ; i < len ; i ++ ) {
            
            a = array[ i ] ;
            n = a >> BLOCK_SHIFT ;
            nn = a & BLOCK_MASK ;
            
            // AND対象位置の条件が0の場合は処理しない.
            if( ( ff = src[ n ] ) != null &&
                ( ff[ nn >> BLOCK_INNER_SHIFT ] & ( 1 << ( nn & BLOCK_INNER_MASK ) ) ) != 0 ) {
                
                if( b != n ) {
                    // ブロック位置条件を取得.
                    if( ( ff = flags[ n ] ) == null ) {
                        ff = new int[ BLOCK_INNER_SIZE ] ;
                        flags[ n ] = ff ;
                    }
                    b = n ;
                }
                
                // フラグセット.
                ff[ nn >> BLOCK_INNER_SHIFT ] = ( ff[ nn >> BLOCK_INNER_SHIFT ] &
                    ~( 1 << ( nn & BLOCK_INNER_MASK ) ) ) |
                    ( ( f & 0x1 ) << ( nn & BLOCK_INNER_MASK ) ) ;
                
            }
        }
    }
    
    /**
     * 全てのフラグをONに設定.
     */
    public final void all() {
        int[] ff,sf ;
        final int len = flags.length ;
        for( int i = 0 ; i < len ; i ++ ) {
            
            // AND条件の情報が存在する場合.
            if( ( sf = src[ i ] ) != null ) {
                
                // ブロック位置条件を取得.
                if( ( ff = flags[ i ] ) == null ) {
                    ff = new int[ BLOCK_INNER_SIZE ] ;
                    flags[ i ] = ff ;
                }
                
                // and条件をコピー.
                System.arraycopy( sf,0,ff,0,BLOCK_INNER_SIZE ) ;
            }
        }
    }
    
    /**
     * 対象の条件をマージ.
     * @param f マージ対象のオブジェクトを設定します.
     */
    public final void marge( final Flags f ) {
        AbstractSmallFlags ss = (AbstractSmallFlags)f ;
        if( ss.max() != max ) {
            throw new MimdbException( "長さが不一致:" + ss.max + " " + max ) ;
        }
        // Flags内のBlockFlags条件を取得.
        int j ;
        int[] ff,bf,sf ;
        final int[][] bff = ss.flags ;
        final int len = bff.length ;
        for( int i = 0 ; i < len ; i ++ ) {
            
            // AND条件の情報が存在する場合.
            // マージ対象のBlockObjectが存在する場合.
            if( ( sf = src[ i ] ) != null && ( bf = bff[ i ] ) != null ) {
                
                // 設定対象のBlockObjectが存在しない場合は作成.
                if( ( ff = flags[ i ] ) == null ) {
                    ff = new int[ BLOCK_INNER_SIZE ] ;
                    flags[ i ] = ff ;
                }
                
                // 対象の条件をORマージ.
                for( j = 0 ; j < BLOCK_INNER_SIZE ; j ++ ) {
                    ff[ j ] |= ( bf[ j ] & sf[ j ] ) ;
                }
            }
        }
    }
}
