package org.maachang.rimdb.search ;

import java.util.Arrays;

import org.maachang.rimdb.index.BetweenPointer;
import org.maachang.rimdb.index.IfPointer;
import org.maachang.rimdb.index.InPointer;
import org.maachang.rimdb.index.Index;
import org.maachang.rimdb.index.LikePointer;
import org.maachang.rimdb.index.LineIndex;
import org.maachang.rimdb.index.MaskFlags;
import org.maachang.rimdb.index.MaskLine;
import org.maachang.rimdb.index.SearchPointer;
import org.maachang.rimdb.table.Indexs;
import org.maachang.rimdb.util.NList;
import org.maachang.rimdb.util.NNChild;
import org.maachang.rimdb.util.NNKeyValue;

/**
 * 検索ブロック.
 * 
 * @version 2014/07/11
 * @author  masahito suzuki
 * @since   rimdb-1.00
 */
@SuppressWarnings("unchecked")
public final class SearchBlock {
    
    protected SearchBlock() {}
    
    /**
     * OR-block処理.
     * @param maskFlags MaskFlagsオブジェクトを設定します.
     * @param indexTable インデックステーブルを設定します.
     * @param list 検索処理済みの検索ポインター群を設定します.
     */
    public static final void orBlock( final MaskFlags maskFlags,final Indexs indexTable,
        final SearchPointer<Index>... list ) {
        final int len = list.length ;
        for( int i = 0 ; i < len ; i ++ ) {
            or( maskFlags,indexTable.getIndex( list[ i ].getColumnName() ),list[ i ] ) ;
        }
    }
    
    /**
     * AND-block.
     * @param maskFlags MaskFlagsオブジェクトを設定します.
     * @param indexTable インデックステーブルを設定します.
     * @param pointer 検索処理済みの検索ポインター群を設定します.
     */
    public static final void andBlock( final MaskFlags maskFlags,final Indexs indexTable,
        final SearchPointer<Index>... pointer ) {
        
        int i ;
        int len = pointer.length ;
        if( len == 0 ) {
            if( maskFlags.getListSize() != 0 ) {
                maskFlags.clear() ;
            }
            return ;
        }
        
        // 1件のみ場合は、ORセット.
        if( len == 1 ) {
            if( pointer[ 0 ].getLength() != 0 ) {
                or( maskFlags,indexTable,pointer[ 0 ] ) ;
            }
            else if( maskFlags.getListSize() != 0 ) {
                maskFlags.clear() ;
            }
            return ;
        }
        
        // 検索ポインタリストコピー.
        SearchPointer<Index>[] list = new SearchPointer[ len ] ;
        System.arraycopy( pointer,0,list,0,len ) ;
        
        // 検索結果件数の少ない順に処理を行う.
        Arrays.sort( list ) ;
        
        // 一番件数の少ない条件がゼロ件の場合.
        if( list[ 0 ].getLength() == 0 ) {
            if( maskFlags.getListSize() != 0 ) {
                maskFlags.clear() ;
            }
            return ;
        }
        
        // 最も少ない条件をOrセット.
        or( maskFlags,indexTable,list[ 0 ] ) ;
        
        // And条件処理.
        boolean eq ;
        Index index ;
        int[] idx,msk,n,lidx ;
        int type,pos,j,lenJ,k,lenK,st,ed ; ;
        NList nn ;
        LineIndex line ;
        MaskLine m ;
        MaskLine[] lm ;
        SearchPointer<Index> p ;
        NNKeyValue base,neq ;
        
        // Andマージ.
        final MaskFlags newMask = new MaskFlags( maskFlags.getMaxLength() ) ;
        for( i = 1 ; i < len ; i ++ ) {
            
            base = maskFlags.getList() ;
            newMask.clear() ;
            p = list[ i ] ;
            index = indexTable.getIndex( p.getColumnName() ) ;
            
            // 最も少ない条件に対して、Mask情報を作成.
            switch( ( type = p.getType() ) ) {
                
                // =?
                case SearchPointer.TYPE_EQ :
                    
                    // 条件が存在する場合.
                    if( ( pos = ((IfPointer)p).position() ) != -1 ) {
                        
                        m = index.getMaskLine( pos ) ;
                        newMask.orMask( m.index,m.maskList ) ;
                        
                    }
                    // 条件が存在しない場合.
                    else {
                        maskFlags.clear() ;
                        return ;
                    }
                    break ;
                    
                // IN(?)
                case SearchPointer.TYPE_IN :
                    
                    n = ((InPointer)p).position() ;
                    lenJ = n.length ;
                    lm = index.getMaskLine() ;
                    for( j = 0 ; j < lenJ ; j ++ ) {
                        
                        if( n[j] == -1 ) {
                            continue ;
                        }
                        
                        m = lm[ n[j] ] ;
                        newMask.orMask( m.index,m.maskList ) ;
                    }
                    break ;
                    
                // >? or >=?
                case SearchPointer.TYPE_BIG :
                case SearchPointer.TYPE_BIG_EQ :
                    
                    // 条件が存在しない場合.
                    if( ( pos = ((IfPointer)p).position() ) == -1 ) {
                        maskFlags.clear() ;
                        return ;
                    }
                    
                    // And元の件数が多い場合は、通常処理.
                    // 今回の検索結果に対して、Andマスクを作成.
                    if( base.size() >= index.getLength() >> 6 ) {
                        
                        lm = index.getMaskLine() ;
                        lenJ = lm.length ;
                        for( j = pos ; j < lenJ ; j ++ ) {
                            
                            m = lm[ j ] ;
                            newMask.orMask( m.index,m.maskList ) ;
                        }
                        
                    }
                    // And元の件数が少ない場合は、LineIndexで処理.
                    // LineIndexでは、And元の検索行位置に対して、LineIndexの行に格納
                    // されている、インデックス位置群から、Maskを作成する.
                    else {
                        
                        eq = type == SearchPointer.TYPE_BIG_EQ ;
                        lm = index.getMaskLine() ;
                        line = index.getLineIndex() ;
                        base.reset() ;
                        while( base.hasNext() ) {
                            
                            lidx = line.getArray( base.next() ) ;
                            if( ( st = line.searchBig( eq,lidx,pos ) ) == -1 ) {
                                continue ;
                            }
                            ed = lidx.length ;
                            
                            for( ; st < ed ; st ++ ) {
                                m = lm[ lidx[ st ] ] ;
                                newMask.orMask( m.index,m.maskList ) ;
                            }
                        }
                        
                    }
                    
                    break ;
                    
                // <? or <=?
                case SearchPointer.TYPE_SMALL :
                case SearchPointer.TYPE_SMALL_EQ :
                    
                    // 条件が存在しない場合.
                    if( ( pos = ((IfPointer)p).position() ) == -1 ) {
                        maskFlags.clear() ;
                        return ;
                    }
                    
                    // And元の件数が多い場合は、通常処理.
                    // 今回の検索結果に対して、Andマスクを作成.
                    if( base.size() >= index.getLength() >> 6 ) {
                        
                        lm = index.getMaskLine() ;
                        for( j = 0 ; j <= pos ; j ++ ) {
                            
                            m = lm[ j ] ;
                            newMask.orMask( m.index,m.maskList ) ;
                        }
                        
                    }
                    // And元の件数が少ない場合は、LineIndexで処理.
                    // LineIndexでは、And元の検索行位置に対して、LineIndexの行に格納
                    // されている、インデックス位置群から、Maskを作成する.
                    else {
                        
                        eq = type == SearchPointer.TYPE_SMALL_EQ ;
                        lm = index.getMaskLine() ;
                        line = index.getLineIndex() ;
                        base.reset() ;
                        while( base.hasNext() ) {
                            
                            lidx = line.getArray( base.next() ) ;
                            if( ( ed = line.searchSmall( eq,lidx,pos ) ) == -1 ) {
                                continue ;
                            }
                            
                            for( st = 0 ; st <= ed ; st ++ ) {
                                m = lm[ lidx[ st ] ] ;
                                newMask.orMask( m.index,m.maskList ) ;
                            }
                        }
                        
                    }
                    
                    break ;
                    
                // !=? or <>?
                case SearchPointer.TYPE_NEQ : 
                    
                    // notEq処理は、他の処理と比較すると、特殊.
                    // 条件が存在しない場合は、そもそも全部が有効になるので、AND元そのまま.
                    // 条件が存在する場合は、その条件以外が有効となるので、条件一致した部分
                    // だけを取得して、反転処理+差分Andマージすれば良い.
                    if( ( pos = ((IfPointer)p).position() ) != -1 ) {
                        
                        // 一旦は=処理と同じ処理.
                        m = index.getMaskLine( pos ) ;
                        idx = m.index ;
                        msk = m.maskList ;
                        lenK = idx.length ;
                        for( k = 0 ; k < lenK ; k ++ ) {
                            newMask.orMask( idx[ k ],msk[ k ] ) ;
                        }
                        
                        // 有効マスクを逆転(not).
                        NNChild ch ;
                        neq = newMask.getList() ;
                        neq.reset() ;
                        while( neq.hasNext() ) {
                            j = neq.next() ;
                            if( ( ch = base.getChild( j ) ) != null ) {
                                ch.o &= ( ~neq.nextValue() ) ;
                            }
                        }
                        neq = null ;
                        
                    }
                    break ;
                    
                // BETWEEN(?)
                case SearchPointer.TYPE_BETWEEN :
                    
                    // 条件が存在しない場合.
                    if( ( pos = ( n = ((BetweenPointer)p).position() )[ 0 ] ) == -1 ) {
                        maskFlags.clear() ;
                        return ;
                    }
                    lenJ = n[ 1 ] ;
                    
                    // And元の件数が多い場合は、通常処理.
                    // 今回の検索結果に対して、Andマスクを作成.
                    if( lenJ - pos > index.getLength() >> 6 ) {
                        
                        lm = index.getMaskLine() ;
                        for( j = pos ; j <= lenJ ; j ++ ) {
                            
                            m = lm[ j ] ;
                            newMask.orMask( m.index,m.maskList ) ;
                        }
                        
                    }
                    // And元の件数が少ない場合は、LineIndexで処理.
                    // LineIndexでは、And元の検索行位置に対して、LineIndexの行に格納
                    // されている、インデックス位置群から、Maskを作成する.
                    else {
                        
                        lm = index.getMaskLine() ;
                        line = index.getLineIndex() ;
                        base.reset() ;
                        while( base.hasNext() ) {
                            
                            lidx = line.getArray( base.next() ) ;
                            if( ( st = line.searchSmall( true,lidx,pos ) ) == -1 ||
                                ( ed = line.searchBig( true,lidx,lenJ ) ) == -1 ) {
                                continue ;
                            }
                            
                            for( ; st <= ed ; st ++ ) {
                                m = lm[ lidx[ st ] ] ;
                                newMask.orMask( m.index,m.maskList ) ;
                            }
                        }
                        
                    }
                    break ;
                    
                // LIKE ?
                case SearchPointer.TYPE_LIKE :
                    
                    // 条件が存在しない場合.
                    if( ( nn = ((LikePointer)p).position() ) == null ||
                        ( lenJ = nn.size() ) == 0 ) {
                        maskFlags.clear() ;
                        return ;
                    }
                    lm = index.getMaskLine() ;
                    for( j = 0 ; j < lenJ ; j ++ ) {
                        
                        m = lm[ nn.get( j ) ] ;
                        newMask.orMask( m.index,m.maskList ) ;
                    }
                    break ;
            }
            
            // この処理はnotEq以外の場合に処理.
            // 今回作成したマージ情報が存在しない場合.
            // 今回作成したMaskをマージして、情報が存在しない場合.
            if( type != SearchPointer.TYPE_NEQ &&
                ( newMask.getList().size() == 0 || maskFlags.and( newMask ) == 0 ) ) {
                maskFlags.clear() ;
                return ;
            }
            
        }
        
    }
    
    /**
     * OR処理.
     * @param maskFlags MaskFlagsオブジェクトを設定します.
     * @param indexTable インデックステーブルを設定します.
     */
    public static final void or( final MaskFlags maskFlags,final Indexs indexTable,
        final SearchPointer<Index> p ) {
        or( maskFlags,indexTable.getIndex( p.getColumnName() ),p ) ;
    }
    
    /**
     * OR処理.
     * @param maskFlags MaskFlagsオブジェクトを設定します.
     * @param index インデックスを設定します.
     */
    public static final void or( final MaskFlags maskFlags,final Index index,
        final SearchPointer<Index> p ) {
        
        int[] n ;
        int pos,j,lenJ ;
        NList nn ;
        NNChild ch ;
        MaskLine[] lm ;
        NNKeyValue lst ;
        
        switch( p.getType() ) {
            
            // =?
            case SearchPointer.TYPE_EQ :
                
                if( ( pos = ((IfPointer)p).position() ) != -1 ) {
                    maskFlags.orMask( index.getMaskLine( pos ) ) ;
                }
                break ;
                
            // IN(?)
            case SearchPointer.TYPE_IN :
                
                n = ((InPointer)p).position() ;
                lenJ = n.length ;
                lm = index.getMaskLine() ;
                for( j = 0 ; j < lenJ ; j ++ ) {
                    if( ( pos = n[j] ) == -1 ) {
                        continue ;
                    }
                    maskFlags.orMask( lm[ pos ] ) ;
                }
                break ;
                
            // >? or >=?
            case SearchPointer.TYPE_BIG :
            case SearchPointer.TYPE_BIG_EQ :
                
                pos = ((IfPointer)p).position() ;
                if( pos == -1 ) {
                    break ;
                }
                lm = index.getMaskLine() ;
                lenJ = lm.length ;
                for( j = pos ; j < lenJ ; j ++ ) {
                    maskFlags.orMask( lm[ j ] ) ;
                }
                break ;
                
            // <? or <=?
            case SearchPointer.TYPE_SMALL :
            case SearchPointer.TYPE_SMALL_EQ :
                
                pos = ((IfPointer)p).position() ;
                if( pos == -1 ) {
                    break ;
                }
                lm = index.getMaskLine() ;
                for( j = 0 ; j <= pos ; j ++ ) {
                    maskFlags.orMask( lm[ j ] ) ;
                }
                break ;
                
            // !=? or <>?
            case SearchPointer.TYPE_NEQ :
                
                // notEqだけ特殊処理.
                // 検索対象が存在しない場合は、全対象となるので、全てのマスクを-1.
                // そうでない場合は、一致条件以外は全てのマスクを-1、
                // そして、一致条件に対しては、対象情報を反転してORマージ.
                
                lenJ = maskFlags.getMaxLength() ;
                lenJ = ( lenJ >> 5 ) + ( ( lenJ & 31 ) == 0 ? 0 : 1 ) ;
                
                pos = ((IfPointer)p).position() ;
                lst = maskFlags.getList() ;
                
                // 検索結果がゼロ件の場合は、全-1をセット.
                if( pos == -1 ) {
                    for( j = 0 ; j < lenJ ; j ++ ) {
                        if( ( ch = lst.getChild( j ) ) == null ) {
                            lst.put( j,-1 ) ;
                        }
                        else {
                            ch.o = -1 ;
                        }
                    }
                }
                // 検索結果が存在する場合は、対象条件以外を-1,それ以外は、
                // 反転条件をorマージ.
                else {
                    
                    Integer v ;
                    final MaskLine m = index.getMaskLine( pos ) ;
                    final int[] idx = m.index ;
                    final int[] mskList = m.maskList ;
                    final NNKeyValue kv = new NNKeyValue() ;
                    final int len = mskList.length ;
                    for( j = 0 ; j < len ; j ++ ) {
                        kv.put( idx[ j ],mskList[ j ] ) ;
                    }
                    for( j = 0 ; j < lenJ ; j ++ ) {
                        if( ( ch = lst.getChild( j ) ) == null ) {
                            ch = lst.put( j,0 ) ;
                        }
                        if( ( v = kv.get( j ) ) == null ) {
                            ch.o = -1 ;
                        }
                        else {
                            ch.o |= ( ~v ) ;
                        }
                    }
                    
                }
                break ;
                
            // BETWEEN(?)
            case SearchPointer.TYPE_BETWEEN :
                
                n = ((BetweenPointer)p).position() ;
                if( ( pos = n[ 0 ] ) == -1 ) {
                    break ;
                }
                lenJ = n[ 1 ] ;
                lm = index.getMaskLine() ;
                for( j = pos ; j <= lenJ ; j ++ ) {
                    maskFlags.orMask( lm[ j ] ) ;
                }
                break ;
                
            // LIKE ?
            case SearchPointer.TYPE_LIKE :
                
                nn = ((LikePointer)p).position() ;
                lenJ = nn.size() ;
                lm = index.getMaskLine() ;
                for( j = 0 ; j < lenJ ; j ++ ) {
                    maskFlags.orMask( lm[ nn.get( j ) ] ) ;
                }
                break ;
                
        }
    }
}
