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 SmallFlagsBase extends AbstractSmallFlags {
    
    /**
     * コンストラクタ.
     */
    public SmallFlagsBase() {
        
    }
    
    /**
     * コンストラクタ.
     * @param len 長さを設定します.
     */
    public SmallFlagsBase( final int len ) {
        create( len ) ;
    }
    
    /**
     * コンストラクタ.
     * @param f オブジェクトをセットします.
     *          渡されたオブジェクトは内部でクリアされます.
     */
    public SmallFlagsBase( final Flags f ) {
        create( f ) ;
    }
    
    /** デストラクタ. **/
    protected void finalize() throws Exception {
        destroy() ;
    }
    
    /**
     * 情報生成.
     * @param len 長さを設定します.
     */
    public final void create( final int len ) {
        int n = ( len >> BLOCK_SHIFT ) + ( ( ( len & BLOCK_MASK ) == 0 ) ? 0 : 1 ) ;
        max = len ;
        flags = new int[ n ][] ;
    }
    
    /**
     * 情報生成.
     * @param f オブジェクトをセットします.
     *          渡されたオブジェクトは内部でクリアされます.
     */
    public final void create( Flags f ) {
        AbstractSmallFlags sf = (AbstractSmallFlags)f ;
        int[][] ff = sf.flags ;
        int m = sf.max ;
        f.destroy() ;
        
        flags = ff ;
        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 = 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[] ff = null ;
        int a,b,n,nn ;
        final int len = array.length ;
        b = -1 ;
        for( int i = 0 ; i < len ; i ++ ) {
            
            a = array[i] ;
            n = a >> BLOCK_SHIFT ;
            nn = a & BLOCK_MASK ;
            
            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 ;
        
        // ブロック位置条件を取得.
        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[] ff = null ;
        int a,b,n,nn ;
        final int len = array.length ;
        b = -1 ;
        for( int i = 0 ; i < len ; i ++ ) {
            
            a = array[i] ;
            n = a >> BLOCK_SHIFT ;
            nn = a & BLOCK_MASK ;
            
            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() {
        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 ) ;
        }
    }
    
    /**
     * 対象の条件をマージ.
     * @param f マージ対象のオブジェクトを設定します.
     */
    public final void marge( final Flags f ) {
        AbstractSmallFlags sf = (AbstractSmallFlags)f ;
        if( sf.max() != max ) {
            throw new MimdbException( "長さが不一致:" + sf.max + " " + max ) ;
        }
        
        // Flags内のBlockFlags条件を取得.
        int j ;
        int[] ff,bf ;
        final int[][] bff = sf.flags ;
        final int len = bff.length ;
        for( int i = 0 ; i < len ; i ++ ) {
            
            // マージ対象のBlockObjectが存在する場合.
            if( ( bf = bff[ i ] ) != null ) {
                
                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 ] ;
                }
            }
        }
    }
    
}
