package org.maachang.mimdb.core.impl ;

import java.util.Arrays;

import org.maachang.mimdb.MimdbException;

/**
 * 基本フラグリスト.
 * 
 * @version 2013/10/27
 * @author masahito suzuki
 * @since MasterInMemDB 1.00
 */
public final class FlagsBase extends Flags {
    
    /**
     * コンストラクタ.
     */
    public FlagsBase() {
        
    }
    
    /**
     * コンストラクタ.
     * @param len 長さを設定します.
     */
    public FlagsBase( final int len ) {
        create( len ) ;
    }
    
    /**
     * コンストラクタ.
     * @param f オブジェクトをセットします.
     *          渡されたオブジェクトは内部でクリアされます.
     */
    public FlagsBase( final Flags f ) {
        create( f ) ;
    }
    
    /** デストラクタ. **/
    protected void finalize() throws Exception {
        destroy() ;
    }
    
    /**
     * 情報生成.
     * @param len 長さを設定します.
     */
    public final void create( final int len ) {
        int n = ( len >> 10 ) + ( ( ( len & 0x3ff ) == 0 ) ? 0 : 1 ) ;
        max = len ;
        flags = new int[ n ][] ;
        clearMinMax() ;
    }
    
    /**
     * 情報生成.
     * @param f オブジェクトをセットします.
     *          渡されたオブジェクトは内部でクリアされます.
     */
    public final void create( Flags f ) {
        int[][] ff = f.flags ;
        int m = f.max ;
        int mn = f.minPos ;
        int mx = f.maxPos ;
        f.destroy() ;
        
        flags = ff ;
        max = m ;
        minPos = mn ;
        maxPos = mx ;
    }
    
    /**
     * 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 >> 10 ;
        final int nn = no & 0x3ff ;
        
        // ブロック位置条件を取得.
        if( ( ff = flags[ n ] ) == null ) {
            ff = new int[ BLOCK_INNER_SIZE ] ;
            flags[ n ] = ff ;
            
            //最小値、最大値をセット.
            minMax( n ) ;
        }
        
        // フラグセット.
        ff[ nn >> 5 ] |= 1 << ( nn & 0x1f ) ;
    }
    
    /**
     * 指定位置のフラグをON.
     * @param array 対象の項番群を設定します.
     */
    public final void addArray( final int[] array ) {
        int[] ff ;
        int a,n,nn ;
        final int len = array.length ;
        for( int i = 0 ; i < len ; i ++ ) {
            
            a = array[i] ;
            n = a >> 10 ;
            nn = a & 0x3ff ;
            
            // ブロック位置条件を取得.
            if( ( ff = flags[ n ] ) == null ) {
                ff = new int[ BLOCK_INNER_SIZE ] ;
                flags[ n ] = ff ;
                
                //最小値、最大値をセット.
                minMax( n ) ;
            }
            
            // フラグセット.
            ff[ nn >> 5 ] |= 1 << ( nn & 0x1f ) ;
        }
    }
    
    /**
     * 指定位置のフラグを設定.
     * @param no 対象の項番を設定します.
     * @param f フラグ条件を設定します.
     *          [0]がOFF、[1]がONです.
     */
    public final void set( final int no,final int f ) {
        int[] ff ;
        final int n = no >> 10 ;
        final int nn = no & 0x3ff ;
        
        // ブロック位置条件を取得.
        if( ( ff = flags[ n ] ) == null ) {
            ff = new int[ BLOCK_INNER_SIZE ] ;
            flags[ n ] = ff ;
            
            //最小値、最大値をセット.
            minMax( n ) ;
        }
        
        // フラグセット.
        ff[ nn >> 5 ] = ( ff[ nn >> 5 ] & ~( 1 << ( nn & 0x1f ) ) ) |
            ( ( f & 0x1 ) << ( nn & 0x1f ) ) ;
    }
    
    /**
     * 指定位置のフラグを設定.
     * @param array 対象の項番群を設定します.
     * @param f フラグ条件を設定します.
     *          [0]がOFF、[1]がONです.
     */
    public final void setArray( final int[] array,final int f ) {
        int[] ff ;
        int a,n,nn ;
        final int len = array.length ;
        for( int i = 0 ; i < len ; i ++ ) {
            
            a = array[i] ;
            n = a >> 10 ;
            nn = a & 0x3ff ;
            
            // ブロック位置条件を取得.
            if( ( ff = flags[ n ] ) == null ) {
                ff = new int[ BLOCK_INNER_SIZE ] ;
                flags[ n ] = ff ;
                
                //最小値、最大値をセット.
                minMax( n ) ;
            }
            
            // フラグセット.
            ff[ nn >> 5 ] = ( ff[ nn >> 5 ] & ~( 1 << ( nn & 0x1f ) ) ) |
                ( ( f & 0x1 ) << ( nn & 0x1f ) ) ;
        }
    }
    
    /**
     * 全てのフラグをONに設定.
     */
    public final void all() {
        final int len = flags.length ;
        for( int i = 0 ; i < len ; i ++ ) {
            
            if( flags[ i ] == null ) {
                flags[ i ] = new int[ BLOCK_INNER_SIZE ] ;
            }
            Arrays.fill( flags[ i ],0xffffffff ) ;
        }
        // 最小値、最大値をセット.
        minPos = 0 ;
        maxPos = len-1 ;
    }
    
    /**
     * 対象の条件をマージ.
     * @param f マージ対象のオブジェクトを設定します.
     */
    public final void marge( final Flags f ) {
        if( f.max() != max ) {
            throw new MimdbException( "長さが不一致:" + f.max + " " + max ) ;
        }
        // マージ元の条件が空の場合.
        if( f.minPos == MIN_DEF ) {
            return ;
        }
        
        // Flags内のBlockFlags条件を取得.
        int j ;
        int[] ff,bf ;
        final int[][] bff = f.flags ;
        final int len = f.maxPos + 1 ;
        for( int i = f.minPos ; i < len ; i ++ ) {
            
            // マージ対象のBlockObjectが存在する場合.
            if( ( bf = bff[ i ] ) != null ) {
                
                if( ( ff = flags[ i ] ) == null ) {
                    ff = new int[ BLOCK_INNER_SIZE ] ;
                    flags[ i ] = ff ;
                    
                    // 最小値、最大値を計算.
                    minMax( i ) ;
                }
                
                // 対象の条件をORマージ.
                for( j = 0 ; j < BLOCK_INNER_SIZE ; j ++ ) {
                    ff[ j ] |= bf[ j ] ;
                }
            }
        }
    }
    
}
