package org.maachang.mimdb.core.impl ;

import java.util.Arrays;

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

/**
 * 巨大行数対応基本フラグリスト.
 * 
 * @version 2013/10/27
 * @author masahito suzuki
 * @since MasterInMemDB 1.03
 */
public final class LargeFlagsBase extends AbstractLargeFlags {
    
    /**
     * コンストラクタ.
     */
    public LargeFlagsBase() {
        
    }
    
    /**
     * コンストラクタ.
     * @param len 長さを設定します.
     */
    public LargeFlagsBase( final int len ) {
        create( len ) ;
    }
    
    /**
     * コンストラクタ.
     * @param f オブジェクトをセットします.
     *          渡されたオブジェクトは内部でクリアされます.
     */
    public LargeFlagsBase( final Flags f ) {
        create( f ) ;
    }
    
    /** デストラクタ. **/
    protected void finalize() throws Exception {
        destroy() ;
    }
    
    /**
     * 情報生成.
     * @param len 長さを設定します.
     */
    public final void create( final int len ) {
        blockLength = ( len >> BLOCK_SHIFT ) + ( ( ( len & BLOCK_MASK ) == 0 ) ? 0 : 1 ) ;
        max = len ;
        list = new NumberKeyValue<int[]>( INIT_LIST_LENGTH ) ;
    }
    
    /**
     * 情報生成.
     * @param f オブジェクトをセットします.
     *          渡されたオブジェクトは内部でクリアされます.
     */
    public final void create( final Flags f ) {
        AbstractLargeFlags lf = (AbstractLargeFlags)f ;
        final NumberKeyValue<int[]> ff = lf.list ;
        final int b = lf.blockLength ;
        final int m = lf.max ;
        f.destroy() ;
        
        list = ff ;
        blockLength = b ;
        max = m ;
    }
    
    /**
     * Andモード取得.
     * @return boolean [true]の場合、ANDモードです.
     */
    public final boolean isAnd() {
        return false ;
    }
    
    /**
     * 指定位置のフラグをON.
     * @param no 対象の項番を設定します.
     */
    public final void add( final int no ) {
        int[] ff ;
        final int n = no >> BLOCK_SHIFT ;
        final int nn = no & BLOCK_MASK ;
        
        // ブロック位置条件を取得.
        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[] ff ;
        int a,b,n,nn ;
        b = -1 ;
        ff = null ;
        final int len = array.length ;
        for( int i = 0 ; i < len ; i ++ ) {
            
            a = array[i] ;
            nn = a & BLOCK_MASK ;
            
            if( b != ( n = a >> BLOCK_SHIFT ) ) {
                
                // ブロック位置条件を取得.
                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 ;
        
        // ブロック位置条件を取得.
        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[] ff ;
        int a,b,n,nn ;
        b = -1 ;
        ff = null ;
        final int len = array.length ;
        for( int i = 0 ; i < len ; i ++ ) {
            
            a = array[i] ;
            nn = a & BLOCK_MASK ;
            
            if( b != ( n = a >> BLOCK_SHIFT ) ) {
                
                // ブロック位置条件を取得.
                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 ;
        final int len = blockLength ;
        for( int i = 0 ; i < len ; i ++ ) {
            
            // ブロック位置条件を取得.
            if( ( ff = list.get( i ) ) == null ) {
                list.put( i,( ff = new int[ BLOCK_INNER_SIZE ] ) ) ;
            }
            Arrays.fill( ff,0xffffffff ) ;
        }
    }
    
    /**
     * 対象の条件をマージ.
     * @param f マージ対象のオブジェクトを設定します.
     */
    public final void marge( final Flags f ) {
        if( f.max() != max ) {
            throw new MimdbException( "長さが不一致:" + f.max() + " " + max ) ;
        }
        AbstractLargeFlags lf = (AbstractLargeFlags)f ;
        
        int n,i ;
        int[] ff,bf ;
        NumberKeyValue<int[]> src = lf.list ;
        src.reset() ;
        while( src.hasNext() ) {
            n = src.next() ;
            bf = src.nextValue() ;
            if( ( ff = list.get( n ) ) == null ) {
                list.put( n,( ff = new int[ BLOCK_INNER_SIZE ] ) ) ;
            }
            
            // 対象の条件をORマージ.
            for( i = 0 ; i < BLOCK_INNER_SIZE ; i ++ ) {
                ff[ i ] |= bf[ i ] ;
            }
        }
    }
    
}
