package org.maachang.rimdb.index ;

import org.maachang.rimdb.util.NAdd;
import org.maachang.rimdb.util.NNChild;
import org.maachang.rimdb.util.NNKeyValue;

/**
 * 検索対象行のマスクフラグ.
 * 
 * @version 2014/06/29
 * @author  masahito suzuki
 * @since   rimdb-1.00
 */
public final class MaskFlags {
    protected NNKeyValue list ;
    protected final int maxLength ;
    
    /**
     * コンストラクタ.
     * @param len 対象テーブル長を設定します.
     */
    public MaskFlags( final int len ) {
        maxLength = len ;
        list = new NNKeyValue() ;
    }
    
    /**
     * 情報クリア.
     */
    public void clear() {
        list.clear() ;
    }
    
    /**
     * MaskFlagsをORセット.
     * @param mask 対象のマスク情報を設定します.
     * @return int サイズが返却されます.
     */
    public final int or( final MaskFlags mask ) {
        int no ;
        NNChild ch,d ;
        NNKeyValue n = mask.list ;
        n.reset() ;
        while( n.hasNext() ) {
            ch = n.nextChild() ;
            if( ( d = list.getChild( no = ch.getKey() ) ) == null ) {
                list.put( no,ch.o ) ;
            }
            else {
                d.o |= ch.o ;
            }
        }
        return list.size() ;
    }
    
    /**
     * MaskFlagsをAndセット.
     * @param mask 対象のマスク情報を設定します.
     * @return int サイズが返却されます.
     */
    public final int and( final MaskFlags mask ) {
        
        int no ;
        NNChild ch,d ;
        NNKeyValue a,b ;
        
        if( mask.list.size() < list.size() ) {
            a = mask.list ;
            b = list ;
        }
        else {
            a = list ;
            b = mask.list ;
        }
        
        if( a.size() == 0 || b.size() == 0 ) {
            list.clear() ;
            return 0 ;
        }
        
        NNKeyValue newList = new NNKeyValue() ;
        a.reset() ;
        while( a.hasNext() ) {
            ch = a.nextChild() ;
            if( ( d = b.getChild( ( no = ch.getKey() ) ) ) != null &&
                ( ch.o & d.o ) != 0 ) {
                newList.put( no,( ch.o & d.o ) ) ;
            }
        }
        list = newList ;
        return list.size() ;
    }
    
    /**
     * orでの行追加.
     * @param no 対象の行位置を設定します.
     */
    public final void orLine( final int no ) {
        NNChild n ;
        final int key = no >> 5 ;
        if( ( n = list.getChild( key ) ) == null ) {
            list.put( key,( 1 << ( no & 31 ) ) ) ;
        }
        else {
            n.o |= 1 << ( no & 31 ) ;
        }
    }
    
    /**
     * orでのマスク追加.
     * @param key 対象の行位置を設定します.
     * @param mask 対象の行マスクを設定します.
     */
    public final void orMask( final int key,final int mask ) {
        NNChild n ;
        if( ( n = list.getChild( key ) ) == null ) {
            list.put( key,mask ) ;
        }
        else {
            n.o |= mask ;
        }
    }
    
    /**
     * orでの行追加.
     * @param list 対象の行位置群を設定します.
     */
    public final void orLine( final int[] noList ) {
        NNChild n ;
        int no,key ;
        final int len = noList.length ;
        for( int i = 0 ; i < len ; i ++ ) {
            if( ( n = list.getChild( ( key = ( no = noList[ i ] ) >> 5 ) ) ) == null ) {
                list.put( key,( 1 << ( no & 31 ) ) ) ;
            }
            else {
                n.o |= 1 << ( no & 31 ) ;
            }
        }
    }
    
    /**
     * orでのマスク追加.
     * @param keyList 対象の行位置群を設定します.
     * @param maskList 対象の行マスク群を設定します.
     */
    public final void orMask( final int[] keyList,final int[] maskList ) {
        NNChild n ;
        int key ;
        final int len = keyList.length ;
        for( int i = 0 ; i < len ; i ++ ) {
            if( ( n = list.getChild( ( key = keyList[ i ] ) ) ) == null ) {
                list.put( key,maskList[ i ] ) ;
            }
            else {
                n.o |= maskList[ i ] ;
            }
        }
    }
    
    /**
     * orでのマスク追加.
     * @param lineIndex 対象の行インデックスを設定します.
     */
    public final void orMask( final MaskLine lineIndex ) {
        
        NNChild n ;
        int key ;
        
        final int[] keyList = lineIndex.index ;
        final int[] maskList = lineIndex.maskList ;
        final int len = keyList.length ;
        
        for( int i = 0 ; i < len ; i ++ ) {
            if( ( n = list.getChild( ( key = keyList[ i ] ) ) ) == null ) {
                list.put( key,maskList[ i ] ) ;
            }
            else {
                n.o |= maskList[ i ] ;
            }
        }
    }
    
    /**
     * 有効行情報を取得.
     * @param out 格納先の情報を設定します.
     */
    public final void get( final NAdd out ) {
        int i,h,v ;
        final int mx = maxLength ;
        list.reset() ;
        while( list.hasNext() ) {
            h = list.next() << 5 ;
            if( ( v = list.nextValue() ) != 0 ) {
                if( h + 32 > mx ) {
                    for( i = 0 ; i < 32 ; i ++ ) {
                        if( ( v & ( 1 << i ) ) != 0 && ( h | i ) < mx ) {
                            out.add( h | i ) ;
                        }
                    }
                }
                else {
                    for( i = 0 ; i < 32 ; i ++ ) {
                        if( ( v & ( 1 << i ) ) != 0 ) {
                            out.add( h | i ) ;
                        }
                    }
                }
            }
        }
    }
    
    /**
     * 有効行数を取得.
     * @return int 有効行数が返却されます.
     */
    public final int size() {
        int i,h,v ;
        int ret = 0 ;
        final int mx = maxLength ;
        list.reset() ;
        while( list.hasNext() ) {
            h = list.next() << 5 ;
            if( ( v = list.nextValue() ) != 0 ) {
                if( h + 32 > mx ) {
                    for( i = 0 ; i < 32 ; i ++ ) {
                        if( ( v & ( 1 << i ) ) != 0 && ( h | i ) < mx ) {
                            ++ ret ;
                        }
                    }
                }
                else if( v == 0xffffffff ) {
                    ret += 32 ;
                }
                else {
                    v = (v & 0x55555555) + (v >> 1 & 0x55555555);
                    v = (v & 0x33333333) + (v >> 2 & 0x33333333);
                    v = (v & 0x0f0f0f0f) + (v >> 4 & 0x0f0f0f0f);
                    v = (v & 0x00ff00ff) + (v >> 8 & 0x00ff00ff);
                    ret += (v & 0x0000ffff) + (v >>16 & 0x0000ffff);
                }
            }
        }
        return ret ;
    }
    
    /**
     * 最大行数を取得.
     * @return int 最大行数が返却されます.
     */
    public final int getMaxLength() {
        return maxLength ;
    }
    
    /**
     * マスク管理オブジェクトを取得.
     * @return NNKeyValue マスク管理オブジェクトが返却されます.
     */
    public final NNKeyValue getList() {
        return list ;
    }
    
    /**
     * リストサイズを取得.
     * @return int リストサイズが返却されます.
     */
    public final int getListSize() {
        return list.size() ;
    }
    
}

