package org.maachang.rimdb.index ;

import java.lang.reflect.Array;
import java.util.List;
import java.util.Map;

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



/**
 * Between検索ポインター.
 * 
 * @version 2014/06/29
 * @author  masahito suzuki
 * @since   rimdb-1.00
 */
@SuppressWarnings("unchecked")
public abstract class BetweenPointer<W> extends SearchPointer<Index<W>> {
    
    /** 一致行. **/
    protected final int[] position = new int[]{ -1,-1 } ;
    
    /** 検索開始ワード. **/
    protected W startWord ;
    
    /** 検索終了ワード. **/
    protected W endWord ;
    
    /**
     * 情報リセット.
     * @param mode [true]を設定した場合、設定ワード条件も削除します.
     */
    public void reset( boolean mode ) {
        super.reset( mode ) ;
        if( mode ) {
            startWord = null ;
            endWord = null ;
        }
        position[ 0 ] = -1 ;
        position[ 1 ] = -1 ;
    }
    
    /**
     * 情報セット.
     * @param start 対象の開始ワード情報を設定します.
     * @param end 対象の終了ワード情報を設定します.
     */
    public void set( W start,W end ) {
        if( start == null || end == null ) {
            throw new RimDbException( "[" + this.getClass().getName() + "]検索条件にnullは設定できません" ) ;
        }
        this.startWord = start ;
        this.endWord = end ;
        this.resetFlag = false ;
    }
    
    /**
     * 情報セット.
     * @param start 対象の開始ワード情報を設定します.
     * @param end 対象の終了ワード情報を設定します.
     */
    public abstract void parameter( Object start,Object end ) ;
    
    /**
     * 情報セット.
     * @param word 対象の検索ワードを設定します.
     */
    public final void parameter( final Object word ) {
        if( word == null ) {
            throw new RimDbException( "[" + this.getClass().getName() + "]検索条件にnullは設定できません" ) ;
        }
        else if( word instanceof List ) {
            List n = (List)word ;
            if( n.size() < 2 ) {
                throw new RimDbException( "[" + this.getClass().getName() + "]検索条件は２つの定義が必要です" ) ;
            }
            parameter( n.get( 0 ),n.get( 1 ) ) ;
        }
        else if( word instanceof Object[] ) {
            Object[] n = (Object[])word ;
            if( n.length < 2 ) {
                throw new RimDbException( "[" + this.getClass().getName() + "]検索条件は２つの定義が必要です" ) ;
            }
            parameter( n[ 0 ],n[ 1 ] ) ;
        }
        else if( word instanceof OList ) {
            OList n = (OList)word ;
            if( n.size() < 2 ) {
                throw new RimDbException( "[" + this.getClass().getName() + "]検索条件は２つの定義が必要です" ) ;
            }
            parameter( n.get( 0 ),n.get( 1 ) ) ;
        }
        else if( word instanceof Map ) {
            Map n = (Map)word ;
            Object s,e ;
            if( ( s = n.get( "start" ) ) == null || ( e = n.get( "end" ) ) == null ) {
                throw new RimDbException( "[" + this.getClass().getName() + "]検索条件は２つの定義[start,end]が必要です" ) ;
            }
            parameter( s,e ) ;
        }
        else if( word.getClass().isArray() ) {
            if( Array.getLength( word ) < 2 ) {
                throw new RimDbException( "[" + this.getClass().getName() + "]検索条件は２つの定義が必要です" ) ;
            }
            parameter( Array.get( word,0 ),Array.get( word,1 ) ) ;
        }
        else {
            throw new RimDbException( "[" + this.getClass().getName() +
                "]検索条件には[" + word.getClass().getName() + "]オブジェクトは定義できません" ) ;
        }
    }
    
    /**
     * インデックス開始位置を取得.
     * @return int[] インデックス開始位置が返却されます.
     */
    public final int[] position() {
        return position ;
    }
    
    /**
     * 検索処理.
     * @param index 対象のインデックスを設定します.
     */
    public final void search( final Index<W> index ) {
        if( resetFlag ) {
            throw new RimDbException( "[" + this.getClass().getName() + "]条件が設定されていません" ) ;
        }
        
        if( !index.between( position,startWord,endWord ) ) {
            
            length = 0 ;
            count = 0 ;
            position[ 0 ] = -1 ;
            position[ 1 ] = -1 ;
        }
        else {
            length = position[ 1 ] - position[ 0 ] ;
            count = ( index.getMaskLine( position[ 1 ] ).count -
                index.getMaskLine( position[ 0 ] ).count ) + 1 ;
        }
    }
    
    /**
     * 検索結果情報をNAdd情報に格納.
     * ※この処理は、検索条件が１件の場合に利用します.
     * @param out 検索結果の行情報をNAdd情報にセットします.
     * @param index 対象のインデックスを設定します.
     */
    public final void get( final NAdd out,final Index<W> index ) {
        final int s = position[ 0 ] ;
        final int e = position[ 1 ] ;
        
        final int[][] indexToLine = ((Index)index).getIndexToLine() ;
        
        int j,lenJ ;
        int[] n ;
        for( int i = s ; i <= e ; i ++ ) {
            n = indexToLine[ i ] ;
            lenJ = n.length ;
            for( j = 0 ; j < lenJ ; j ++ ) {
                out.add( n[ j ] ) ;
            }
        }
    }
    
    /** ベースコピー. **/
    protected void baseCopy( final boolean mode,final BetweenPointer o ) {
        super.baseCopy( mode,o ) ;
        if( !mode ) {
            o.startWord = startWord ;
            o.endWord = endWord ;
            o.resetFlag = false ;
        }
        o.position[ 0 ] = -1 ;
        o.position[ 1 ] = -1 ;
    }
    
}
