package org.maachang.mimdb.core.impl ;

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

/**
 * 巨大行数対応フラグリスト.
 * 
 * @version 2013/10/27
 * @author masahito suzuki
 * @since MasterInMemDB 1.03
 */
public abstract class AbstractLargeFlags implements Flags {
    
    /** 対象オブジェクト適応開始値. **/
    protected static final int TARGET_LENGTH = 32768 ;
    
    /** フラグ管理初期サイズ. **/
    protected static final int INIT_LIST_LENGTH = 4 ;
    
    /** 最大管理データ数. **/
    protected int max ;
    
    /** ブロック最大数. **/
    protected int blockLength ;
    
    /** フラグ管理情報. **/
    protected NumberKeyValue<int[]> list ;
    
    /**
     * 情報破棄.
     */
    public void destroy() {
        list = null ;
        max = 0 ;
        blockLength = 0 ;
    }
    
    /**
     * 管理情報をクリア.
     */
    public final void clear() {
        list = new NumberKeyValue<int[]>( INIT_LIST_LENGTH ) ;
    }
    
    /**
     * Andモード取得.
     * @return boolean [true]の場合、ANDモードです.
     */
    public abstract boolean isAnd() ;
    
    /**
     * 指定位置のフラグをON.
     * @param no 対象の項番を設定します.
     */
    public abstract void add( final int no ) ;
    
    /**
     * 指定位置のフラグをON.
     * @param array 対象の項番群を設定します.
     */
    public abstract void addArray( final int[] array ) ;
    
    /**
     * 指定位置のフラグを設定.
     * @param no 対象の項番を設定します.
     * @param f フラグ条件を設定します.
     *          [0]がOFF、[1]がONです.
     */
    public abstract void set( final int no,final int f ) ;
    
    /**
     * 指定位置のフラグを設定.
     * @param array 対象の項番群を設定します.
     * @param f フラグ条件を設定します.
     *          [0]がOFF、[1]がONです.
     */
    public abstract void setArray( final int[] array,final int f ) ;
    
    /**
     * 全てのフラグをONに設定.
     */
    public abstract void all() ;
    
    /**
     * 対象の条件をマージ.
     * @param f マージ対象のオブジェクトを設定します.
     */
    public abstract void marge( final Flags f ) ;
    
    /**
     * 指定位置のフラグを取得.
     * @param no 対照の項番を設定します.
     * @return int フラグ条件が返却されます.
     *          [0]がOFF、[1]がONです.
     */
    public final int get( final int no ) {
        final int[] ff ;
        
        // ブロック位置条件が存在する場合のみ、情報取得.
        if( ( ff = list.get( no >> BLOCK_SHIFT ) ) != null ) {
            
            // フラグ条件を返却.
            final int n = no & BLOCK_MASK ;
            return ( ff[ n >> BLOCK_INNER_SHIFT ] & ( 1 << ( n & BLOCK_INNER_MASK ) ) ) >>
                ( n & BLOCK_INNER_MASK ) ;
        }
        // ブロック位置条件が存在しない場合は0を返却.
        return 0 ;
    }
    
    /**
     * 最大データ数を取得.
     * @return int データ数が返却されます.
     */
    public final int max() {
        return max ;
    }
    
    /**
     * フラグがONの件数を取得.
     * @return int フラグがONの件数が返却されます.
     */
    public final int size() {
        if( list.size() == 0 ) {
            return 0 ;
        }
        
        int[] ff ;
        int n,i,j,c,code ;
        int ret = 0 ;
        list.reset() ;
        while( list.hasNext() ) {
            n = list.next() ;
            ff = list.nextValue() ;
            
            // 最終ブロック位置.
            if( n + 1 == blockLength ) {
                final int mx = max - 32 ;
                for( i = 0 ; i < BLOCK_INNER_SIZE ; i ++ ) {
                    if( ( ( n << BLOCK_SHIFT ) | ( i << BLOCK_INNER_SHIFT ) ) >= mx ) {
                        if( ff[ i ] == 0 ) {
                            break ;
                        }
                        code = ff[ i ] ;
                        c = ( n << BLOCK_SHIFT ) | ( i << BLOCK_INNER_SHIFT ) ;
                        for( j = 0 ; j < 32 ; j ++ ) {
                            if( ( c | j ) >= max ) {
                                break ;
                            }
                            if( ( code & ( 1 << j ) ) != 0 ) {
                                ret ++ ;
                            }
                        }
                        break ;
                    }
                    else if( ff[ i ] != 0 ) {
                        code = ff[ i ] ;
                        code = (code & 0x55555555) + (code >> 1 & 0x55555555);
                        code = (code & 0x33333333) + (code >> 2 & 0x33333333);
                        code = (code & 0x0f0f0f0f) + (code >> 4 & 0x0f0f0f0f);
                        code = (code & 0x00ff00ff) + (code >> 8 & 0x00ff00ff);
                        ret += (code & 0x0000ffff) + (code >>16 & 0x0000ffff);
                    }
                }
            }
            // 最終ブロック位置以外.
            else {
                for( i = 0 ; i < BLOCK_INNER_SIZE ; i ++ ) {
                    if( ff[ i ] != 0 ) {
                        // ビット数のONの合計を取得.
                        code = ff[ i ] ;
                        code = (code & 0x55555555) + (code >> 1 & 0x55555555);
                        code = (code & 0x33333333) + (code >> 2 & 0x33333333);
                        code = (code & 0x0f0f0f0f) + (code >> 4 & 0x0f0f0f0f);
                        code = (code & 0x00ff00ff) + (code >> 8 & 0x00ff00ff);
                        ret += (code & 0x0000ffff) + (code >>16 & 0x0000ffff);
                    }
                }
            }
        }
        
        return ret ;
    }
    
    /**
     * 検索結果情報を取得.
     * @param out 対象のオブジェクトが設定されます.
     * @return int 取得件数が返却されます.
     */
    public final int getResultArray( final ResultArray[] out ) {
        if( list.size() == 0 || out.length == 0) {
            return 0 ;
        }
        
        int[] ff ;
        int n,i,j,c,code ;
        int ret = 0 ;
        final int oLen = out.length ;
        
        list.reset() ;
        while( list.hasNext() ) {
            n = list.next() ;
            ff = list.nextValue() ;
            
            // 最終ブロック位置.
            if( n + 1 == blockLength ) {
                
                final int mx = max - 32 ;
                for( i = 0 ; i < BLOCK_INNER_SIZE ; i ++ ) {
                    if( ( c = ( n << BLOCK_SHIFT ) | ( i << BLOCK_INNER_SHIFT ) ) >= mx ) {
                        if( ff[ i ] == 0 ) {
                            break ;
                        }
                        code = ff[ i ] ;
                        for( j = 0 ; j < 32 ; j ++ ) {
                            if( ( c | j ) >= max ) {
                                break ;
                            }
                            if( ( code & ( 1 << j ) ) != 0 ) {
                                out[ ret ++ ].no = ( c | j ) ;
                                if( ret == oLen ) {
                                    return ret ;
                                }
                            }
                        }
                        break ;
                    }
                    else if( ff[ i ] != 0 ) {
                        
                        if( ret + 32 >= oLen ) {
                            
                            if( ff[ i ] == 0xffffffff ) {
                                for( j = 0 ; j < 32 ; j ++ ) {
                                    out[ ret ++ ].no = ( c | j ) ;
                                    if( ret == oLen ) {
                                        return ret ;
                                    }
                                }
                            }
                            else {
                                code = ff[ i ] ;
                                for( j = 0 ; j < 32 ; j ++ ) {
                                    if( ( code & ( 1 << j ) ) != 0 ) {
                                        out[ ret ++ ].no = ( c | j ) ;
                                        if( ret == oLen ) {
                                            return ret ;
                                        }
                                    }
                                }
                            }
                            
                        }
                        else if( ff[ i ] == 0xffffffff ) {
                            for( j = 0 ; j < 32 ; j ++ ) {
                                out[ ret ++ ].no = ( c | j ) ;
                            }
                        }
                        else {
                            code = ff[ i ] ;
                            for( j = 0 ; j < 32 ; j ++ ) {
                                if( ( code & ( 1 << j ) ) != 0 ) {
                                    out[ ret ++ ].no = ( c | j ) ;
                                }
                            }
                        }
                        
                    }
                }
            }
            // 最終ブロック位置以外.
            else {
                for( i = 0 ; i < BLOCK_INNER_SIZE ; i ++ ) {
                    if( ff[ i ] != 0 ) {
                        
                        c = ( n << BLOCK_SHIFT ) | ( i << BLOCK_INNER_SHIFT ) ;
                        if( ret + 32 >= oLen ) {
                            
                            if( ff[ i ] == 0xffffffff ) {
                                for( j = 0 ; j < 32 ; j ++ ) {
                                    out[ ret ++ ].no = ( c | j ) ;
                                    if( ret == oLen ) {
                                        return ret ;
                                    }
                                }
                            }
                            else {
                                code = ff[ i ] ;
                                for( j = 0 ; j < 32 ; j ++ ) {
                                    if( ( code & ( 1 << j ) ) != 0 ) {
                                        out[ ret ++ ].no = ( c | j ) ;
                                        if( ret == oLen ) {
                                            return ret ;
                                        }
                                    }
                                }
                            }
                            
                        }
                        else if( ff[ i ] == 0xffffffff ) {
                            for( j = 0 ; j < 32 ; j ++ ) {
                                out[ ret ++ ].no = ( c | j ) ;
                            }
                        }
                        else {
                            code = ff[ i ] ;
                            for( j = 0 ; j < 32 ; j ++ ) {
                                if( ( code & ( 1 << j ) ) != 0 ) {
                                    out[ ret ++ ].no = ( c | j ) ;
                                }
                            }
                        }
                        
                    }
                }
            }
        }
        
        return ret ;
    }
    
    /**
     * 検索結果情報を取得.
     * @param out 対象のオブジェクトが設定されます.
     * @param off 対象のオフセット値を設定します.
     * @return int 取得件数が返却されます.
     */
    public final int getResultArray( final ResultArray[] out,final int off ) {
        if( list.size() == 0 || out.length == 0) {
            return 0 ;
        }
        else if( off <= 0 ) {
            return getResultArray( out ) ;
        }
        
        int[] ff ;
        int n,i,j,c,code,npos ;
        int pos = 0 ;
        int cnt = 0 ;
        final int oLen = out.length ;
        
        list.reset() ;
        while( list.hasNext() ) {
            n = list.next() ;
            ff = list.nextValue() ;
            
            // 最終ブロック位置.
            if( n + 1 == blockLength ) {
                final int mx = max - 32 ;
                for( i = 0 ; i < BLOCK_INNER_SIZE ; i ++ ) {
                    if( ( c = ( n << BLOCK_SHIFT ) | ( i << BLOCK_INNER_SHIFT ) ) >= mx ) {
                        if( ff[ i ] == 0 ) {
                            break ;
                        }
                        code = ff[ i ] ;
                        for( j = 0 ; j < 32 ; j ++ ) {
                            if( ( c | j ) >= max ) {
                                break ;
                            }
                            if( ( code & ( 1 << j ) ) != 0 ) {
                                if( ( pos += 1 ) > off ) {
                                    out[ cnt ++ ].no = ( c | j ) ;
                                    if( cnt == oLen ) {
                                        return cnt ;
                                    }
                                }
                            }
                        }
                        break ;
                    }
                    else if( ff[ i ] != 0 ) {
                        
                        if( cnt + 32 >= oLen ) {
                            
                            if( ff[ i ] == 0xffffffff ) {
                                for( j = 0 ; j < 32 ; j ++ ) {
                                    if( ( pos += 1 ) > off ) {
                                        out[ cnt ++ ].no = ( c | j ) ;
                                        if( cnt == oLen ) {
                                            return cnt ;
                                        }
                                    }
                                }
                            }
                            else {
                                code = ff[ i ] ;
                                for( j = 0 ; j < 32 ; j ++ ) {
                                    if( ( code & ( 1 << j ) ) != 0 ) {
                                        if( ( pos += 1 ) > off ) {
                                            out[ cnt ++ ].no = ( c | j ) ;
                                            if( cnt == oLen ) {
                                                return cnt ;
                                            }
                                        }
                                    }
                                }
                            }
                            
                        }
                        else if( ff[ i ] == 0xffffffff ) {
                            for( j = 0 ; j < 32 ; j ++ ) {
                                if( ( pos += 1 ) > off ) {
                                    out[ cnt ++ ].no = ( c | j ) ;
                                }
                            }
                        }
                        else {
                            code = ff[ i ] ;
                            for( j = 0 ; j < 32 ; j ++ ) {
                                if( ( code & ( 1 << j ) ) != 0 ) {
                                    if( ( pos += 1 ) > off ) {
                                        out[ cnt ++ ].no = ( c | j ) ;
                                    }
                                }
                            }
                        }
                    }
                }
                
            }
            // 最終ブロック位置以外.
            else {
                for( i = 0 ; i < BLOCK_INNER_SIZE ; i ++ ) {
                    if( ff[ i ] != 0 ) {
                        
                        // オフセット以前の条件の場合.
                        if( pos < off ) {
                            
                            // フラグ数を計算して、オフセット値を超えるかチェックする.
                            code = ff[ i ] ;
                            code = (code & 0x55555555) + (code >> 1 & 0x55555555);
                            code = (code & 0x33333333) + (code >> 2 & 0x33333333);
                            code = (code & 0x0f0f0f0f) + (code >> 4 & 0x0f0f0f0f);
                            code = (code & 0x00ff00ff) + (code >> 8 & 0x00ff00ff);
                            npos = (code & 0x0000ffff) + (code >>16 & 0x0000ffff);
                            
                            // オフセット値を超えない場合.
                            if( npos + pos < off ) {
                                pos += npos ;
                                continue ;
                            }
                        }
                        // オフセット値を越えている場合.
                        else {
                            
                            c = ( n << BLOCK_SHIFT ) | ( i << BLOCK_INNER_SHIFT ) ;
                            if( cnt + 32 >= oLen ) {
                                
                                if( ff[ i ] == 0xffffffff ) {
                                    for( j = 0 ; j < 32 ; j ++ ) {
                                        out[ cnt ++ ].no = ( c | j ) ;
                                        if( cnt == oLen ) {
                                            return cnt ;
                                        }
                                    }
                                }
                                else {
                                    code = ff[ i ] ;
                                    for( j = 0 ; j < 32 ; j ++ ) {
                                        if( ( code & ( 1 << j ) ) != 0 ) {
                                            out[ cnt ++ ].no = ( c | j ) ;
                                            if( cnt == oLen ) {
                                                return cnt ;
                                            }
                                        }
                                    }
                                }
                                
                            }
                            else if( ff[ i ] == 0xffffffff ) {
                                for( j = 0 ; j < 32 ; j ++ ) {
                                    out[ cnt ++ ].no = ( c | j ) ;
                                }
                            }
                            else {
                                code = ff[ i ] ;
                                for( j = 0 ; j < 32 ; j ++ ) {
                                    if( ( code & ( 1 << j ) ) != 0 ) {
                                        out[ cnt ++ ].no = ( c | j ) ;
                                    }
                                }
                            }
                            
                            continue ;
                        }
                        
                        // オフセットの半ばの場合.
                        c = ( n << BLOCK_SHIFT ) | ( i << BLOCK_INNER_SHIFT ) ;
                        if( cnt + 32 >= oLen ) {
                            
                            if( ff[ i ] == 0xffffffff ) {
                                for( j = 0 ; j < 32 ; j ++ ) {
                                    if( ( pos += 1 ) > off ) {
                                        out[ cnt ++ ].no = ( c | j ) ;
                                        if( cnt == oLen ) {
                                            return cnt ;
                                        }
                                    }
                                }
                            }
                            else {
                                code = ff[ i ] ;
                                for( j = 0 ; j < 32 ; j ++ ) {
                                    if( ( code & ( 1 << j ) ) != 0 ) {
                                        if( ( pos += 1 ) > off ) {
                                            out[ cnt ++ ].no = ( c | j ) ;
                                            if( cnt == oLen ) {
                                                return cnt ;
                                            }
                                        }
                                    }
                                }
                            }
                            
                        }
                        else if( ff[ i ] == 0xffffffff ) {
                            for( j = 0 ; j < 32 ; j ++ ) {
                                if( ( pos += 1 ) > off ) {
                                    out[ cnt ++ ].no = ( c | j ) ;
                                }
                            }
                        }
                        else {
                            code = ff[ i ] ;
                            for( j = 0 ; j < 32 ; j ++ ) {
                                if( ( code & ( 1 << j ) ) != 0 ) {
                                    if( ( pos += 1 ) > off ) {
                                        out[ cnt ++ ].no = ( c | j ) ;
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        
        return cnt ;
    }
}
