package org.maachang.mimdb.core.impl ;

import org.maachang.mimdb.core.ResultArray;

/**
 * フラグリスト.
 * 
 * @version 2013/10/27
 * @author masahito suzuki
 * @since MasterInMemDB 1.00
 */
public abstract class Flags {
    
    /** １ブロックサイズ. **/
    protected static final int BLOCK_SIZE = 1024 ;
    
    /** １ブロックの内部配列数. **/
    protected static final int BLOCK_INNER_SIZE = 32 ;
    
    /** 書換最小デフォルト値. **/
    protected static final int MIN_DEF = Integer.MAX_VALUE ;
    
    /** 最大管理データ数. **/
    protected int max ;
    
    /** フラグ管理情報. **/
    protected int[][] flags ;
    
    /** 書換最小位置. **/
    protected int minPos = MIN_DEF ;
    
    /** 書換最大位置. **/
    protected int maxPos = -1 ;
    
    /**
     * 情報破棄.
     */
    public void destroy() {
        flags = null ;
        max = 0 ;
        clearMinMax() ;
    }
    
    /**
     * 管理情報をクリア.
     */
    public final void clear() {
        flags = new int[ flags.length ][] ;
        clearMinMax() ;
    }
    
    /**
     * min,maxクリア.
     */
    protected final void clearMinMax() {
        minPos = MIN_DEF ;
        maxPos = -1 ;
    }
    
    /**
     * 最小値、最大値を計算.
     */
    protected final void minMax( final int n ) {
        if( n < minPos ) {
            minPos = n ;
        }
        if( n > maxPos ) {
            maxPos = n ;
        }
    }
    
    /**
     * 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 ) {
        // 情報は存在しない.
        if( minPos == MIN_DEF ) {
            return 0 ;
        }
        final int[] ff ;
        
        // ブロック位置条件が存在する場合のみ、情報取得.
        if( ( ff = flags[ no >> 10 ] ) != null ) {
            
            // フラグ条件を返却.
            final int n = no & 0x3ff ;
            return ( ff[ n >> 5 ] & ( 1 << ( n & 0x1f ) ) ) >> ( n & 0x1f ) ;
        }
        // ブロック位置条件が存在しない場合は0を返却.
        return 0 ;
    }
    
    /**
     * 最大データ数を取得.
     * @return int データ数が返却されます.
     */
    public final int max() {
        return max ;
    }
    
    /**
     * フラグがONの件数を取得.
     * @return int フラグがONの件数が返却されます.
     */
    public final int size() {
        // 情報は存在しない.
        if( minPos == MIN_DEF ) {
            return 0 ;
        }
        
        int j,k,n,c,ret ;
        int[] ff ;
        ret = 0 ;
        
        final int[][] f = flags ;
        final int len = maxPos + 1 ;
        
        // 今回の処理で終端がMAXを越える可能性がある場合.
        if( ( len << 10 ) >= max ) {
            
            final int mx = max - 32 ;
            for( int i = minPos ; i < len ; i ++ ) {
                if( ( ff = f[ i ] ) != null ) {
                    for( j = 0 ; j < BLOCK_INNER_SIZE ; j ++ ) {
                        if( ( n = ff[ j ] ) != 0 ) {
                            
                            // 今回の処理で最大値を越える検出が行われる場合.
                            if( (( i << 10 ) | ( j << 5 )) >= mx ) {
                                c = ( i << 10 ) | ( j << 5 ) ;
                                for( k = 0 ; k < 32 ; k ++ ) {
                                    
                                    // 対象条件で長さを越える検出の場合.
                                    if( ( c | k ) >= max ) {
                                        return ret ;
                                    }
                                    if( ( n & ( 1 << k ) ) != 0 ) {
                                        ret ++ ;
                                    }
                                }
                            }
                            // ON条件が存在する場合.
                            else {
                                // ビット数のONの合計を取得.
                                n = (n & 0x55555555) + (n >> 1 & 0x55555555);
                                n = (n & 0x33333333) + (n >> 2 & 0x33333333);
                                n = (n & 0x0f0f0f0f) + (n >> 4 & 0x0f0f0f0f);
                                n = (n & 0x00ff00ff) + (n >> 8 & 0x00ff00ff);
                                ret += (n & 0x0000ffff) + (n >>16 & 0x0000ffff);
                            }
                            
                        }
                    }
                }
            }
            
        }
        // 今回の処理で終端がMAXを越えない場合.
        else {
            
            for( int i = minPos ; i < len ; i ++ ) {
                if( ( ff = f[ i ] ) != null ) {
                    for( j = 0 ; j < BLOCK_INNER_SIZE ; j ++ ) {
                        if( ( n = ff[ j ] ) != 0 ) {
                            
                            // ビット数のONの合計を取得.
                            n = (n & 0x55555555) + (n >> 1 & 0x55555555);
                            n = (n & 0x33333333) + (n >> 2 & 0x33333333);
                            n = (n & 0x0f0f0f0f) + (n >> 4 & 0x0f0f0f0f);
                            n = (n & 0x00ff00ff) + (n >> 8 & 0x00ff00ff);
                            ret += (n & 0x0000ffff) + (n >>16 & 0x0000ffff);
                            
                        }
                    }
                }
            }
        }
        return ret ;
    }
    
    /**
     * 検索結果情報を取得.
     * @param out 対象のオブジェクトが設定されます.
     * @return int 取得件数が返却されます.
     */
    public final int getResultArray( final ResultArray[] out ) {
        // 情報は存在しない.
        if( minPos == MIN_DEF || out.length == 0 ) {
            return 0 ;
        }
        
        int j,k,n,c,cnt ;
        int[] ff ;
        cnt = 0 ;
        final int[][] f = flags ;
        final int len = maxPos + 1 ;
        final int oLen = out.length ;
        
        // 今回の処理で終端がMAXを越える可能性がある場合.
        if( ( len << 10 ) >= max ) {
            
            final int mx = max - 32 ;
            for( int i = minPos ; i < len ; i ++ ) {
                if( ( ff = f[ i ] ) != null ) {
                    for( j = 0 ; j < BLOCK_INNER_SIZE ; j ++ ) {
                        if( ( n = ff[ j ] ) != 0 ) {
                            
                            // 今回の処理で最大値を越える検出が行われる場合.
                            if( ( c = ( i << 10 ) | ( j << 5 ) ) >= mx ) {
                                for( k = 0 ; k < 32 ; k ++ ) {
                                    
                                    // 対象条件で長さを越える検出の場合.
                                    if( ( c | k ) >= max ) {
                                        return cnt ;
                                    }
                                    if( ( n & ( 1 << k ) ) != 0 ) {
                                        out[ cnt ++ ].no = ( c | k ) ;
                                        if( cnt == oLen ) {
                                            return cnt ;
                                        }
                                    }
                                }
                            }
                            // 全部がON条件の場合.
                            else if( n == 0xffffffff ) {
                                if( cnt + 32 > oLen ) {
                                    for( k = 0 ; k < 32 ; k ++ ) {
                                        out[ cnt ++ ].no = ( c | k ) ;
                                        if( cnt == oLen ) {
                                            return cnt ;
                                        }
                                    }
                                }
                                else {
                                    for( k = 0 ; k < 32 ; k ++ ) {
                                        out[ cnt ++ ].no = ( c | k ) ;
                                    }
                                }
                            }
                            // ON条件が存在する場合.
                            else {
                                if( cnt + 32 > oLen ) {
                                    for( k = 0 ; k < 32 ; k ++ ) {
                                        if( ( n & ( 1 << k ) ) != 0 ) {
                                            out[ cnt ++ ].no = ( c | k ) ;
                                            if( cnt == oLen ) {
                                                return cnt ;
                                            }
                                        }
                                    }
                                }
                                else {
                                    for( k = 0 ; k < 32 ; k ++ ) {
                                        if( ( n & ( 1 << k ) ) != 0 ) {
                                            out[ cnt ++ ].no = ( c | k ) ;
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        
        // 最大値を越えない場合.
        else {
            
            for( int i = minPos ; i < len ; i ++ ) {
                if( ( ff = f[ i ] ) != null ) {
                    for( j = 0 ; j < BLOCK_INNER_SIZE ; j ++ ) {
                        if( ( n = ff[ j ] ) != 0 ) {
                            c = ( i << 10 ) | ( j << 5 ) ;
                            
                            // 全部がON条件の場合.
                            if( n == 0xffffffff ) {
                                if( cnt + 32 > oLen ) {
                                    for( k = 0 ; k < 32 ; k ++ ) {
                                        out[ cnt ++ ].no = ( c | k ) ;
                                        if( cnt == oLen ) {
                                            return cnt ;
                                        }
                                    }
                                }
                                else {
                                    for( k = 0 ; k < 32 ; k ++ ) {
                                        out[ cnt ++ ].no = ( c | k ) ;
                                    }
                                }
                            }
                            // ON条件が存在する場合.
                            else {
                                if( cnt + 32 > oLen ) {
                                    for( k = 0 ; k < 32 ; k ++ ) {
                                        if( ( n & ( 1 << k ) ) != 0 ) {
                                            out[ cnt ++ ].no = ( c | k ) ;
                                            if( cnt == oLen ) {
                                                return cnt ;
                                            }
                                        }
                                    }
                                }
                                else {
                                    for( k = 0 ; k < 32 ; k ++ ) {
                                        if( ( n & ( 1 << k ) ) != 0 ) {
                                            out[ cnt ++ ].no = ( c | k ) ;
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        return cnt ;
    }
    
    /**
     * 検索結果情報を取得.
     * @param out 対象のオブジェクトが設定されます.
     * @param off 対象のオフセット値を設定します.
     * @return int 取得件数が返却されます.
     */
    public final int getResultArray( final ResultArray[] out,final int off ) {
        // 情報は存在しない.
        if( minPos == MIN_DEF || out.length == 0 ) {
            return 0 ;
        }
        
        int j,k,n,c,cnt ;
        int[] ff ;
        cnt = 0 ;
        final int[][] f = flags ;
        final int len = maxPos + 1 ;
        final int oLen = out.length ;
        
        // 今回の処理で終端がMAXを越える可能性がある場合.
        if( ( len << 10 ) >= max ) {
            
            int cursor = 0 ;
            final int mx = max - 32 ;
            for( int i = minPos ; i < len ; i ++ ) {
                if( ( ff = f[ i ] ) != null ) {
                    for( j = 0 ; j < BLOCK_INNER_SIZE ; j ++ ) {
                        if( ( n = ff[ j ] ) != 0 ) {
                            
                            // 今回の処理で最大値を越える検出が行われる場合.
                            if( ( c = ( i << 10 ) | ( j << 5 ) ) >= mx ) {
                                for( k = 0 ; k < 32 ; k ++ ) {
                                    
                                    // 対象条件で長さを越える検出の場合.
                                    if( ( c | k ) >= max ) {
                                        return cnt ;
                                    }
                                    
                                    // 条件が存在する場合.
                                    // オフセットカーソルを越えた場合は情報セット.
                                    if( ( n & ( 1 << k ) ) != 0 && cursor ++ >= off ) {
                                        out[ cnt ++ ].no = ( c | k ) ;
                                        if( cnt == oLen ) {
                                            return cnt ;
                                        }
                                    }
                                }
                            }
                            // 全部がON条件の場合.
                            else if( n == 0xffffffff ) {
                                
                                // カーソル位置がオフセット値を完全に超えている場合.
                                if( cursor >= off ) {
                                    for( k = 0 ; k < 32 ; k ++ ) {
                                        out[ cnt ++ ].no = ( c | k ) ;
                                        if( cnt == oLen ) {
                                            return cnt ;
                                        }
                                    }
                                }
                                // この処理でカーソル位置がオフセットを越える場合.
                                else if( cursor + 32 >= off ) {
                                    for( k = 0 ; k < 32 ; k ++ ) {
                                        
                                        // オフセットカーソルを越えた場合は情報セット.
                                        if( cursor ++ >= off ) {
                                            out[ cnt ++ ].no = ( c | k ) ;
                                            if( cnt == oLen ) {
                                                return cnt ;
                                            }
                                        }
                                    }
                                }
                                // カーソル位置がオフセットを越えない場合.
                                else {
                                    cursor += 32 ;
                                }
                            }
                            // ON条件が存在する場合.
                            else {
                                
                                // カーソル位置がオフセット値を完全に超えている場合.
                                if( cursor >= off ) {
                                    for( k = 0 ; k < 32 ; k ++ ) {
                                        if( ( n & ( 1 << k ) ) != 0 ) {
                                            out[ cnt ++ ].no = ( c | k ) ;
                                            if( cnt == oLen ) {
                                                return cnt ;
                                            }
                                        }
                                    }
                                }
                                // カーソル位置がオフセット値を越えていない場合.
                                else {
                                    
                                    for( k = 0 ; k < 32 ; k ++ ) {
                                        
                                        // 条件が存在する場合.
                                        // オフセットカーソルを越えた場合は情報セット.
                                        if( ( n & ( 1 << k ) ) != 0 && cursor ++ >= off ) {
                                            out[ cnt ++ ].no = ( c | k ) ;
                                            if( cnt == oLen ) {
                                                return cnt ;
                                            }
                                        }
                                    }
                                    
                                }
                            }
                        }
                    }
                }
            }
        }
        
        // 最大値を越えない場合.
        else {
            
            int cursor = 0 ;
            for( int i = minPos ; i < len ; i ++ ) {
                if( ( ff = f[ i ] ) != null ) {
                    for( j = 0 ; j < BLOCK_INNER_SIZE ; j ++ ) {
                        if( ( n = ff[ j ] ) != 0 ) {
                            
                            c = ( i << 10 ) | ( j << 5 ) ;
                            
                            // 全部がON条件の場合.
                            if( n == 0xffffffff ) {
                                
                                // カーソル位置がオフセット値を完全に超えている場合.
                                if( cursor >= off ) {
                                    for( k = 0 ; k < 32 ; k ++ ) {
                                        out[ cnt ++ ].no = ( c | k ) ;
                                        if( cnt == oLen ) {
                                            return cnt ;
                                        }
                                    }
                                }
                                // この処理でカーソル位置がオフセットを越える場合.
                                else if( cursor + 32 >= off ) {
                                    for( k = 0 ; k < 32 ; k ++ ) {
                                        
                                        // オフセットカーソルを越えた場合は情報セット.
                                        if( cursor ++ >= off ) {
                                            out[ cnt ++ ].no = ( c | k ) ;
                                            if( cnt == oLen ) {
                                                return cnt ;
                                            }
                                        }
                                    }
                                }
                                // カーソル位置がオフセットを越えない場合.
                                else {
                                    cursor += 32 ;
                                }
                            }
                            // ON条件が存在する場合.
                            else {
                                
                                // カーソル位置がオフセット値を完全に超えている場合.
                                if( cursor >= off ) {
                                    for( k = 0 ; k < 32 ; k ++ ) {
                                        if( ( n & ( 1 << k ) ) != 0 ) {
                                            out[ cnt ++ ].no = ( c | k ) ;
                                            if( cnt == oLen ) {
                                                return cnt ;
                                            }
                                        }
                                    }
                                }
                                // カーソル位置がオフセット値を越えていない場合.
                                else {
                                    
                                    for( k = 0 ; k < 32 ; k ++ ) {
                                        
                                        // 条件が存在する場合.
                                        // オフセットカーソルを越えた場合は情報セット.
                                        if( ( n & ( 1 << k ) ) != 0 && cursor ++ >= off ) {
                                            out[ cnt ++ ].no = ( c | k ) ;
                                            if( cnt == oLen ) {
                                                return cnt ;
                                            }
                                        }
                                    }
                                    
                                }
                            }
                        }
                    }
                }
            }
        }
        return cnt ;
    }
}
