package org.maachang.rimdb.index.string ;

import org.maachang.rimdb.RimDbException;
import org.maachang.rimdb.util.NAdd;

/**
 * Like検索.
 *
 * @version 2014/07/04
 * @author  masahito suzuki
 * @since   rimdb-1.00
 */
final class SearchLike {
    
    /** [Like]Likeパーサーの生成. **/
    protected static final Object[] create( final String like ) throws Exception {
        
        final LikeParser p = LikeAnalyzer.parser( like ) ;
        final Object[] o = p.parser ;
        final int len = o.length ;
        
        for( int i = 0 ; i < len ; i ++ ) {
            if( o[ i ] instanceof String ) {
                o[ i ] = new IString( ( String )o[ i ] ) ;
            }
        }
        return o ;
    }
    
    /** Like検索 **/
    protected static final void search( final NAdd result,final IString[] indexs,final Object[] ps )
        throws Exception {
        
        int len = ps.length ;
        int allLen = indexs.length ;
        
        // 通常パース条件.
        int n = -99 ; // 定義不明.
        int rLen,j,p ;
        IString v,value,bw ;
        
        // 最後方チェック条件.
        boolean last = false ;
        
        // 各場所保存条件を設定.
        LikePositionList list = null ;
        LikePositionList newList = null ;
        
        // 前回のindexof検索文字.
        bw = null ;
        
        // 通常パース開始.
        for( int i = 0 ; i < len ; i ++ ) {
            
            // 条件指定.
            if( ps[ i ] instanceof Integer ) {
                
                // 最後方検索条件の場合.
                if( (Integer)ps[ i ] == LikeParser.POS_LAST ) {
                    last = true ;
                }
                // 通常条件 or 検索条件.
                else {
                    n = (Integer)ps[ i ] ;
                }
            }
            // 文字指定.
            else {
                
                v = (IString)ps[ i ] ;
                // N文字指定の場合.
                if( n > 0 ) {
                    
                    if( list == null ) {
                        list = new LikePositionList( allLen ) ;
                    }
                    newList = list.newList() ;
                    rLen = list.length() ;
                    
                    // 最後方一致で検索する場合.
                    if( last ) {
                        // 前回の条件で、indexof検索が行われていない場合.
                        if( bw == null ) {
                            
                            for( j = 0 ; j < rLen ; j ++ ) {
                                
                                // 条件不一致.
                                if( ( value = indexs[ list.position( j ) ] ).length() == list.offset( j ) + n + v.length() &&
                                    value.equals( list.offset( j ) + n,v,0,v.length() ) ) {
                                    // 条件一致.
                                    newList.add( list.position( j ),list.offset( j ) + n + v.length() ) ;
                                }
                            }
                        }
                        // 前回の条件で、indexof検索が行われた場合.
                        else {
                            
                            int bwLen = bw.length ;
                            for( j = 0 ; j < rLen ; j ++ ) {
                                
                                value = indexs[ list.position( j ) ] ;
                                
                                while( true ) {
                                    
                                    // 条件不一致.
                                    if( value.length() == list.offset( j ) + n + v.length() &&
                                        value.equals( list.offset( j ) + n,v,0,v.length() ) ) {
                                        
                                        // 条件一致.
                                        newList.add( list.position( j ),list.offset( j ) + n + v.length() ) ;
                                        break ;
                                    }
                                    // 前回のワードで再検索.
                                    if( ( p = value.indexOf( bw,list.offset( j ) - ( bwLen - 1 ) ) ) != -1 ) {
                                        list.offset( j,p+bwLen ) ;
                                    }
                                    else {
                                        
                                        // 検索対象は存在しない.
                                        break ;
                                    }
                                }
                            }
                        }
                        
                        // 最終確認処理は行わない.
                        last = false ;
                    }
                    // 通常一致.
                    else {
                        
                        // 前回の条件で、indexof検索が行われていない場合.
                        if( bw == null ) {
                            
                            for( j = 0 ; j < rLen ; j ++ ) {
                                
                                value = indexs[ list.position( j ) ] ;
                                
                                // 条件不一致.
                                if( value.length() >= list.offset( j ) + n + v.length() &&
                                    value.equals( list.offset( j ) + n,v,0,v.length() ) ) {
                                    
                                    // 条件一致.
                                    newList.add( list.position( j ),list.offset( j ) + n + v.length() ) ;
                                }
                            }
                        }
                        // 前回の条件で、indexof検索が行われた場合.
                        else {
                            
                            for( j = 0 ; j < rLen ; j ++ ) {
                                
                                value = indexs[ list.position( j ) ] ;
                                
                                while( true ) {
                                    
                                    // 条件不一致.
                                    if( value.length() >= list.offset( j ) + n + v.length() &&
                                        value.equals( list.offset( j ) + n,v,0,v.length() ) ) {
                                        
                                        // 条件一致.
                                        newList.add( list.position( j ),list.offset( j ) + n + v.length() ) ;
                                        break ;
                                    }
                                    // 前回のワードで再検索.
                                    if( ( p = value.indexOf( bw,list.offset( j ) ) ) != -1 ) {
                                        list.offset( j,p+bw.length ) ;
                                    }
                                    else {
                                        // 検索対象は存在しない.
                                        break ;
                                    }
                                }
                            }
                        }
                    }
                    
                    // データ切り替え.
                    list = newList ;
                    newList = null ;
                    n = -99 ;// 定義不明.
                    
                    // 前回のindexof検索条件をクリア.
                    bw = null ;
                }
                // 条件指定の場合.
                // ただし、指定より、最後方チェックの方が優先される.
                else if( !last ) {
                    
                    switch( n ) {
                        
                        case LikeParser.POS_FIRST : // 先頭一致.
                            
                            newList = new LikePositionList( false,allLen ) ;
                            rLen = allLen ;
                            
                            // 先頭検索のみ、インデックスの最適位置で処理できる.
                            j = StringIndex.searchBS( false,indexs,v ) ;
                            
                            // 開始位置で一致しない場合は、次から検索対象とする.
                            if( !indexs[ j ].startsWith( v ) ) {
                                ++ j ;
                            }
                            for( ; j < rLen ; j ++ ) {
                                if( indexs[ j ].startsWith( v ) ) {
                                    newList.add( j,v.length() ) ;
                                }
                                else {
                                    
                                    // 以降は存在しないので、処理を抜ける.
                                    break ;
                                }
                            }
                            // データ切り替え.
                            list = newList ;
                            newList = null ;
                            break ;
                        
                        case LikeParser.POS_BETWEEN : // 次の文字列が一致.
                            
                            if( list != null ) {
                                
                                newList = list.newList() ;
                                rLen = list.length() ;
                                
                                for( j = 0 ; j < rLen ; j ++ ) {
                                    
                                    if( ( p = indexs[ list.position( j ) ].indexOf( v,list.offset( j ) ) ) != -1 ) {
                                        newList.add( list.position( j ),p + v.length() ) ;
                                    }
                                }
                            }
                            else {
                                
                                newList = new LikePositionList( false,allLen ) ;
                                rLen = allLen ;
                                
                                for( j = 0 ; j < rLen ; j ++ ) {
                                    
                                    if( ( p = indexs[ j ].indexOf( v,0 ) ) != -1 ) {
                                        newList.add( j,p + v.length() ) ;
                                    }
                                }
                            }
                            // 前回のindexof検索条件としてセット.
                            bw = v ;
                            
                            // データ切り替え.
                            list = newList ;
                            newList = null ;
                            break ;
                        
                        case -99 :
                            throw new RimDbException( "Like構文が不正です" ) ;
                    }
                }
                // 最後方チェック条件の場合.
                if( last ) {
                    
                    if( list != null ) {
                        
                        newList = list.newList() ;
                        rLen = list.length() ;
                        
                        for( j = 0 ; j < rLen ; j ++ ) {
                            
                            if( indexs[ list.position( j ) ].length == list.offset( j ) + 1 + v.length() &&
                                indexs[ list.position( j ) ].endsWith( v ) ) {
                                
                                newList.add( list.position( j ),v.length() ) ;
                            }
                        }
                    }
                    // 前回の条件で、indexof検索が行われた場合.
                    else {
                        
                        newList = new LikePositionList( false,allLen ) ;
                        rLen = allLen ;
                        
                        for( j = 0 ; j < rLen ; j ++ ) {
                            
                            if( indexs[ j ].endsWith( v ) ) {
                                newList.add( j,v.length() ) ;
                            }
                        }
                    }
                    
                    // データ切り替え.
                    list = newList ;
                    newList = null ;
                    
                    // 前回のindexof検索条件をクリア.
                    bw = null ;
                    break ;
                }
                // 検索条件がゼロ件の場合.
                if( list == null || list.length() <= 0 ) {
                    return ;
                }
            }
        }
        
        // 検索条件がゼロ件の場合.
        if( list == null || list.length() <= 0 ) {
            return ;
        }
        // N文字指定の場合.
        else if( n > 0 ) {
            
            newList = list.newList() ;
            rLen = list.length() ;
            
            // 前回の条件で、indexof検索が行われていない場合.
            if( bw == null ) {
                
                for( j = 0 ; j < rLen ; j ++ ) {
                    
                    // 条件一致
                    if( indexs[ list.position( j ) ].length() == list.offset( j ) + n ) {
                        
                        // 条件一致.
                        newList.add( list.position( j ),0 ) ;
                    }
                }
            }
            else {
                
                int bwLen = bw.length ;
                
                // 条件一致
                for( j = 0 ; j < rLen ; j ++ ) {
                    
                    value = indexs[ list.position( j ) ] ;
                    
                    while( true ) {
                        
                        // 条件一致
                        if( value.length() == list.offset( j ) + n ) {
                            
                            // 条件一致.
                            newList.add( list.position( j ),0 ) ;
                            break ;
                        }
                        
                        // 前回のワードで再検索.
                        if( ( p = value.indexOf( bw,list.offset( j ) - ( bwLen - 1 ) ) ) != -1 ) {
                            
                            list.offset( j,p+bwLen ) ;
                        }
                        else {
                            
                            // 検索対象は存在しない.
                            break ;
                        }
                    }
                }
            }
            
            // データ切り替え.
            list = newList ;
            newList = null ;
            
            // 検索条件がゼロ件の場合.
            if( list.length() <= 0 ) {
                return ;
            }
        }
        list.list( result ) ;
    }
    
    /** NGramでの検索. **/
    protected static final void searchNGram( final NAdd result,final Object ngram,final Object[] ps )
        throws Exception {
        IString[] indexs = ((NGram.NGramIndex)ngram).src ;
        int len = ps.length ;
        int allLen = indexs.length ;
        
        // 通常パース条件.
        int n = -99 ; // 定義不明.
        IString v ;
        
        // 各場所保存条件を設定.
        LikePositionList list = null ;
        LikePositionList newList = null ;
        
        // 最後方チェック条件.
        boolean last = false ;
        
        // 前回のindexof検索文字.
        IString bw = null ;
        
        // 通常パース開始.
        for( int i = 0 ; i < len ; i ++ ) {
            // 条件指定.
            if( ps[ i ] instanceof Integer ) {
                // 最後方検索条件の場合.
                if( (Integer)ps[ i ] == LikeParser.POS_LAST ) {
                    last = true ;
                }
                // 通常条件 or 検索条件.
                else {
                    n = (Integer)ps[ i ] ;
                }
            }
            // 文字指定.
            else {
                v = (IString)ps[ i ] ;
                // N文字指定の場合.
                if( n > 0 ) {
                    // リスト条件が設定されていない場合.
                    if( list == null ) {
                        list = new LikePositionList( allLen ) ;
                    }
                    // 最後方一致で検索する場合.
                    if( last ) {
                        // 現在ポジションから続く、指定文字の検索.
                        newList = NGram.eqaulsPosition( true,list,ngram,v,n,bw ) ;
                        // 最終確認処理は行わない.
                        last = false ;
                    }
                    // 通常一致検索.
                    else {
                        // 現在ポジションから続く、指定文字の検索.
                        newList = NGram.eqaulsPosition( false,list,ngram,v,n,bw ) ;
                    }
                    
                    // データ切り替え.
                    list = newList ;
                    newList = null ;
                    n = -99 ;// 定義不明.
                    bw = null ; // 前回のindexof条件をクリア.
                }
                // 条件指定の場合.
                // ただし、指定より、最後方チェックの方が優先される.
                else if( !last ) {
                    switch( n ) {
                        case LikeParser.POS_FIRST : // 先頭一致.
                            list = NGram.startsWith( ngram,v ) ;
                            break ;
                        case LikeParser.POS_BETWEEN : // 次の文字列が一致.
                            list = NGram.indexOf( list,ngram,v ) ;
                            bw = v ; // 前回のindexof条件をセット.
                            break ;
                        case -99 :
                            throw new RimDbException( "Like構文が不正です" ) ;
                    }
                }
                // 最後方チェック条件の場合.
                if( last ) {
                    list = NGram.endsWith( list,ngram,v ) ;
                    bw = null ; // 前回のindexof条件をクリア.
                    break ;
                }
                // 検索条件がゼロ件の場合.
                if( list == null || list.length() <= 0 ) {
                    return ;
                }
            }
        }
        // 検索条件がゼロ件の場合.
        if( list == null || list.length() <= 0 ) {
            return ;
        }
        // N文字指定の場合.
        else if( n > 0 ) {
            // 長さが一致する条件のみ処理.
            ((LikePositionList)list).endOffset( n,indexs,bw ) ;
            
            // 検索条件がゼロ件の場合.
            if( list.length() <= 0 ) {
                return ;
            }
        }
        list.list( result ) ;
    }
}
