package org.maachang.mimdb.core.impl ;

import org.maachang.mimdb.MimdbException;
import org.maachang.mimdb.core.util.NumberKeyValue;


/**
 * 巨大行数対応Andフラグリスト.
 * 
 * @version 2013/10/27
 * @author masahito suzuki
 * @since MasterInMemDB 1.03
 */
public final class LargeFlagsAnd extends AbstractLargeFlags {
    
    /** And元フラグ. **/
    private NumberKeyValue<int[]> src ;
    
    /**
     * コンストラクタ.
     */
    public LargeFlagsAnd() {
        
    }
    
    /**
     * コンストラクタ.
     * @param f And元のオブジェクトをセットします.
     *          渡されたオブジェクトは内部でクリアされます.
     */
    public LargeFlagsAnd( final Flags f ) {
        create( f ) ;
    }
    
    /** デストラクタ. **/
    protected void finalize() throws Exception {
        destroy() ;
    }
    
    /**
     * 情報生成.
     * @param f And元のオブジェクトをセットします.
     *          渡されたオブジェクトは内部でクリアされます.
     */
    public final void create( final Flags f ) {
        AbstractLargeFlags lf = (AbstractLargeFlags)f ;
        NumberKeyValue<int[]> s = lf.list ;
        final int b = lf.blockLength ;
        final int m = lf.max ;
        f.destroy() ;
        
        src = s ;
        max = m ;
        blockLength = b ;
        list = new NumberKeyValue<int[]>( INIT_LIST_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.get( n ) ) != null &&
            ( ff[ nn >> BLOCK_INNER_SHIFT ] & ( 1 << ( nn & BLOCK_INNER_MASK ) ) ) != 0 ) {
            
            // ブロック位置条件を取得.
            if( ( ff = list.get( n ) ) == null ) {
                list.put( n,( ff = new int[ BLOCK_INNER_SIZE ] ) ) ;
            }
            
            // フラグセット.
            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[] sf,ff ;
        b = -1 ;
        ff = null ;
        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( ( sf = src.get( n ) ) != null &&
                ( sf[ nn >> BLOCK_INNER_SHIFT ] & ( 1 << ( nn & BLOCK_INNER_MASK ) ) ) != 0 ) {
                
                if( b != n ) {
                    
                    // ブロック位置条件を取得.
                    if( ( ff = list.get( n ) ) == null ) {
                        list.put( n,( ff = new int[ BLOCK_INNER_SIZE ] ) ) ;
                    }
                    
                    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.get( n ) ) != null &&
            ( ff[ nn >> BLOCK_INNER_SHIFT ] & ( 1 << ( nn & BLOCK_INNER_MASK ) ) ) != 0 ) {
            
            // ブロック位置条件を取得.
            if( ( ff = list.get( n ) ) == null ) {
                list.put( n,( ff = new int[ BLOCK_INNER_SIZE ] ) ) ;
            }
            
            // フラグセット.
            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[] sf,ff ;
        b = -1 ;
        ff = null ;
        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( ( sf = src.get( n ) ) != null &&
                ( sf[ nn >> BLOCK_INNER_SHIFT ] & ( 1 << ( nn & BLOCK_INNER_MASK ) ) ) != 0 ) {
                
                if( b != n ) {
                    
                    // ブロック位置条件を取得.
                    if( ( ff = list.get( n ) ) == null ) {
                        list.put( n,( ff = new int[ BLOCK_INNER_SIZE ] ) ) ;
                    }
                    
                    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 = blockLength ;
        for( int i = 0 ; i < len ; i ++ ) {
            
            // AND条件の情報が存在する場合.
            if( ( sf = src.get( i ) ) != null ) {
                
                // ブロック位置条件を取得.
                if( ( ff = list.get( i ) ) == null ) {
                    list.put( i,( ff = new int[ BLOCK_INNER_SIZE ] ) ) ;
                }
                
                // and条件をコピー.
                System.arraycopy( sf,0,ff,0,BLOCK_INNER_SIZE ) ;
            }
        }
    }
    
    /**
     * 対象の条件をマージ.
     * @param f マージ対象のオブジェクトを設定します.
     */
    public final void marge( final Flags f ) {
        if( f.max() != max ) {
            throw new MimdbException( "長さが不一致:" + f.max() + " " + max ) ;
        }
        AbstractLargeFlags lf = (AbstractLargeFlags)f ;
        if( lf.list.size() == 0 ) {
            return ;
        }
        
        // Flags内のBlockFlags条件を取得.
        int n,i ;
        int[] ff,bf,sf ;
        NumberKeyValue<int[]> flist = lf.list ;
        flist.reset() ;
        while( flist.hasNext() ) {
            n = flist.next() ;
            if( ( sf = src.get( n ) ) != null ) {
                
                // ブロック位置条件を取得.
                if( ( ff = list.get( n ) ) == null ) {
                    list.put( n,( ff = new int[ BLOCK_INNER_SIZE ] ) ) ;
                }
                
                // マージ.
                bf = flist.nextValue() ;
                for( i = 0 ; i < BLOCK_INNER_SIZE ; i ++ ) {
                    ff[ i ] |= ( bf[ i ] & sf[ i ] ) ;
                }
                
            }
        }
    }
}
