package org.maachang.rimdb.index ;

import java.util.Arrays;

import org.maachang.rimdb.util.NList;
import org.maachang.rimdb.util.OList;
import org.maachang.rimdb.util.OOKeyValue;

/**
 * インデックス作成オブジェクト.
 * 
 * @version 2014/06/29
 * @author  masahito suzuki
 * @since   rimdb-1.00
 */
@SuppressWarnings("unchecked")
public abstract class CreateIndex<T,O> {
    
    /** インデックス作成用. **/
    private static final class SrcIndex<T> implements Comparable<Object> {
        T value ;
        NList lines = new NList() ;
        public int compareTo( Object n ) {
            return ((Comparable)value).compareTo( ((SrcIndex)n).value ) ;
        }
    }
    
    /** データ格納用. **/
    protected OList<T> list = null ;
    
    /** NGramインデックス作成用. **/
    protected boolean ngram ;
    
    /**
     * 情報クリア.
     */
    public void clear() {
        if( list != null ) {
            list.clear() ;
        }
        list = null ;
    }
    
    /**
     * インデックス行情報をセット.
     * @param value 対象のデータを設定します.
     */
    public void add( T value ) {
        list.add( value ) ;
    }
    
    /**
     * NGramインデックスを許可するか設定.
     * @param flg [true]の場合、NGramインデックスを定義します.
     */
    public void setNGram( boolean flg ) {
        ngram = flg ;
    }
    
    /**
     * インデックス情報を生成.
     * @return O 生成されたインデックスオブジェクトが返却されます.
     */
    public O create() {
        
        int i ;
        T v ;
        SrcIndex<T> n ;
        int len = list.size() ;
        final int max = len ;
        
        // 情報を集約.
        OOKeyValue<T,SrcIndex<T>> idx = new OOKeyValue<T,SrcIndex<T>>() ;
        for( i = 0 ; i < len ; i ++ ) {
            
            v = list.get( i ) ;
            if( ( n = idx.get( v ) ) == null ) {
                n = new SrcIndex<T>() ;
                n.value = v ;
                idx.put( v,n ) ;
            }
            n.lines.add( i ) ;
        }
        clear() ;
        
        // 集約された情報を配列化.
        int cnt = 0 ;
        len = idx.size() ;
        SrcIndex<T>[] target = new SrcIndex[ len ] ;
        idx.reset() ;
        while( idx.hasNext() ) {
            target[ cnt++ ] = idx.nextValue() ;
        }
        idx = null ;
        
        // 集約情報配列をソート.
        Arrays.sort( target ) ;
        
        // 配列化.
        final Object index = create( len ) ;
        final int[][] lines = new int[ len ][] ;
        
        for( i = 0 ; i < len ; i ++ ) {
            set( index,i,( n = target[ i ] ).value ) ;
            lines[ i ] = n.lines.getArray() ;
        }
        target = null ;
        
        // インデックス変換.
        return convert( index,ngram,lines,max ) ;
    }
    
    /**
     * 配列生成.
     * @param length 配列生成サイズを設定します.
     * @return Object 生成された配列が返却されます.
     */
    protected abstract Object create( int length ) ;
    
    /**
     * 配列セット.
     * @param index 対象のインデックスを設定します.
     * @param no 対象の項番を設定します.
     * @param value 対象の条件を設定します.
     */
    protected abstract void set( Object index,int no,Object value ) ;
    
    /**
     * インデックス変換.
     * @param index 対象のインデックスを設定します.
     * @param lines 対象の行インデックスを設定します.
     * @param max 全件数を設定します.
     * @return O 生成されたインデックスオブジェクトが返却されます.
     */
    protected abstract O convert( Object index,int[][] lines,int max ) ;
    
    /**
     * インデックス変換.
     * @param index 対象のインデックスを設定します.
     * @param ngram Like検索用のNGramを有効にする場合は[true]を設定します.
     * @param lines 対象の行インデックスを設定します.
     * @param max 全件数を設定します.
     * @return StringIndex 生成されたインデックスオブジェクトが返却されます.
     */
    protected O convert( Object index,boolean ngram,int[][] lines,int max ) {
        return convert( index,lines,max ) ;
    }
}
