package org.maachang.rimdb.index.string ;

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

/**
 * String(IString)インデックス.
 * 
 * @version 2014/07/04
 * @author  masahito suzuki
 * @since   rimdb-1.00
 */
public final class StringIndex extends Index<IString> {
    
    /**
     * 完全一致検索.
     * @param a 対象の配列を設定します.
     * @param key 対象のキーを設定します.
     * @return int 項番が返却されます.
     *             [-1]が返却された場合、情報は存在しません.
     */
    public static final int search( final IString[] a, final IString key) {
        int low = 0 ;
        int high = a.length -1 ;
        int mid,cmp ;
        while (low <= high) {
            if ((cmp = a[mid = (low + high) >>> 1].compareTo(key)) < 0) {
                low = mid + 1;
            }
            else if (cmp > 0) {
                high = mid - 1;
            }
            else {
                return mid;
            }
        }
        return -1 ;
    }
    
    /**
     * 大なり小なり検索.
     * @param big [true]の場合、第三引数が第二引数の検知条件より大きい条件(>=)を対象とします.
     * @param a 対象の配列を設定します.
     * @param key 対象のキーを設定します.
     * @return int 項番が返却されます.
     *             [-1]が返却された場合、情報は存在しません.
     */
    public static final int searchBS( final boolean big,final IString[] a,final IString key ) {
        int low = 0 ;
        int high = a.length -1 ;
        int mid,cmp ;
        mid = 0 ;
        while (low <= high) {
            if ((cmp = a[mid = (low + high) >>> 1].compareTo(key)) < 0) {
                low = mid + 1;
            }
            else if (cmp > 0) {
                high = mid - 1;
            }
            else {
                return mid;
            }
        }
        if( big ) {
            if( a[mid].compareTo( key ) > 0 ) {
                return mid ;
            }
            else if( a.length <= mid + 1 ) {
                return a.length-1 ;
            }
            return mid + 1 ;
        }
        if( a[mid].compareTo( key ) > 0 ) {
            if( mid <= 0 ) {
                return 0 ;
            }
            return mid -1 ;
        }
        return mid ;
    }
    
    /** インデックス情報. **/
    protected final IString[] index ;
    
    /** NGramインデックス. **/
    protected final Object ngram ;
    
    /**
     * コンストラクタ.
     * @param index 対象のインデックスを設定します.
     * @param line 対象の行情報を設定します.
     * @param max 全体長を設定します.
     */
    public StringIndex( IString[] index,Object ngram,int[][] line,int max ) {
        super( line,max ) ;
        this.index = index ;
        this.ngram = ngram ;
    }
    
    /**
     * インデックス情報を取得.
     * @return IString[] インデックス情報が返却されます.
     */
    public IString[] getIndex() {
        return index ;
    }
    
    /**
     * NGramインデックス情報を取得.
     * @return Object NGramインデックス情報が返却されます.
     */
    public Object getNgram() {
        return ngram ;
    }
    
    /**
     * 一致検索.
     * @param key 対象のキーを設定します.
     * @return int インデックス番号が返却されます.
     */
    public final int eq( final IString key ) {
        return search( index,key ) ;
    }
    
    /**
     * 大なり検索.
     * @param eq 一致を許可する場合には[true]をセットします.
     * @param key 対象のキーを設定します.
     * @return int インデックス番号が返却されます.
     */
    public final int big( final boolean eq,final IString key ) {
        if( index[ index.length-1 ].compareTo( key ) == -1 ) {
            return -1 ;
        }
        else if( eq ) {
            return searchBS( true,index,key ) ;
        }
        else {
            int n = searchBS( true,index,key ) ;
            if( key.equals( index[ n ] ) && (++n) >= maxLine ) {
                return -1 ;
            }
            return n ;
        }
    }
    
    /**
     * 小なり検索.
     * @param eq 一致を許可する場合には[true]をセットします.
     * @param key 対象のキーを設定します.
     * @return int インデックス番号が返却されます.
     */
    public final int small( final boolean eq,final IString key ) {
        if( index[ 0 ].compareTo( key ) == 1 ) {
            return -1;
        }
        else if( eq ) {
            return searchBS( false,index,key ) ;
        }
        else {
            int n = searchBS( false,index,key ) ;
            if( key.equals( index[ n ] ) && (--n) < 0 ) {
                return -1 ;
            }
            return n ;
        }
    }
    
    /**
     * between検索.
     * @param out 格納行情報を設定します.
     * @param start 開始値を設定します.
     * @param end 終了値を設定します.
     * @return boolean [true]の場合、範囲取得に成功しました.
     */
    public final boolean between( final int[] out,IString start,IString end ) {
        if( start.compareTo( end ) == 1 ) {
            IString n = start ;
            start = end ;
            end = n ;
        }
        out[ 0 ] = searchBS( true,index,start ) ;
        out[ 1 ] = searchBS( false,index,end ) ;
        
        final int f = index[ out[ 0 ] ].compareTo( start ) ;
        final int e = index[ out[ 1 ] ].compareTo( end ) ;
        return ( f >= 0 || e >= 0 ) && ( f <= 0 || e <= 0 ) ;
    }
    
    /**
     * 部分一致検索.
     * @param result 一致行情報を格納するListオブジェクトを設定します.
     * @param key 対象のLike条件を設定します.
     */
    public final void like( final NAdd result,String key ) {
        try {
            if( ngram != null ) {
                
                // NGramサーチ.
                SearchLike.searchNGram( result,ngram,SearchLike.create( key ) ) ;
            }
            else {
                
                // ノーマルサーチ.
                SearchLike.search( result,index,SearchLike.create( key ) ) ;
            }
        } catch( RimDbException ri ) {
            throw ri ;
        } catch( Exception e ) {
            throw new RimDbException( e ) ;
        }
    }
}

