package org.maachang.mimdb.core ;

import java.util.Arrays;

import org.maachang.mimdb.MimdbException;
import org.maachang.mimdb.core.util.ObjectLinkedList;

/**
 * 結果情報の行ポインタ管理.
 * 
 * @version 2013/10/10
 * @author masahito suzuki
 * @since MasterInMemDB 1.00
 */
@SuppressWarnings("unchecked")
class ResultPointer {
    
    /** ポインタ要素. **/
    private final class PointerElement extends ResultArray implements Comparable<PointerElement> {
        
        /** コンストラクタ. **/
        public PointerElement() {
        }
        
        /** コンストラクタ. **/
        public PointerElement( final int n ) {
            no = n ;
        }
        
        /**
         * 比較処理.
         * @param n 比較条件を設定します.
         * @return int 比較結果が返却されます.
         *            マイナスの場合、第一引数の方が大きい.
         *            プラスの場合、第一引数の方が小さい.
         *            0の場合、第一引数と同じ.
         */
        public final int compareTo( final PointerElement n ) {
            
            int p ;
            int[] idx ;
            Object c,cc ;
            
            final int[][] idxList = sort.indexList ;
            final int[] sortNoList = sort.sortNoList ;
            final boolean[] desc = sort.desc ;
            final int nno = n.no ;
            
            // 後ろから判別.
            for( int i = idxList.length-1 ; i >= 0 ; i -- ) {
                
                // 降順.
                if( desc[ i ] ) {
                    
                    // 対象条件がインデックスの場合.
                    if( ( idx = idxList[ i ] ) != null ) {
                        
                        // ソート番号でソート処理.
                        if( ( p = idx[ nno ] - idx[ no ] ) != 0 ) {
                            return p ;
                        }
                    }
                    // 対象条件がインデックスではない場合.
                    else {
                        
                        // 条件が不一致の場合.
                        if( ( c = src[ no ].getValue( sortNoList[ i ] ) ) !=
                            ( cc = src[ nno ].getValue( sortNoList[ i ] ) ) ) {
                            
                            // nullチェック.
                            if( c == null || cc == null ) {
                                if( c == null ) {
                                    return 1 ;
                                }
                                return -1 ;
                            }
                            if( ( p = ((Comparable)cc).compareTo( c ) ) != 0 ) {
                                return p ;
                            }
                        }
                    }
                }
                // 昇順.
                else {
                    
                    // 対象条件がインデックスの場合.
                    if( ( idx = idxList[ i ] ) != null ) {
                        
                        // ソート番号でソート処理.
                        if( ( p = idx[ no ] - idx[ nno ] ) != 0 ) {
                            return p ;
                        }
                    }
                    // 対象条件がインデックスではない場合.
                    else {
                        
                        // 条件が不一致の場合.
                        if( ( c = src[ no ].getValue( sortNoList[ i ] ) ) !=
                            ( cc = src[ nno ].getValue( sortNoList[ i ] ) ) ) {
                            
                            // nullチェック.
                            if( c == null || cc == null ) {
                                if( c == null ) {
                                    return -1 ;
                                }
                                return 1 ;
                            }
                            if( ( p = ((Comparable)c).compareTo( cc ) ) != 0 ) {
                                return p ;
                            }
                        }
                    }
                }
            }
            return 0 ;
        }
    }
    
    /** 最大バケットソート対応件数. **/
    private static final int MAX_BACKET_SORT_LENGTH = 65535 ;
    
    /** データソース. **/
    protected MimdbRow[] src ;
    
    /** ソート要素. **/
    protected SortElement sort ;
    
    /** 検索結果条件. **/
    protected MimdbMiddleSearch msearch ;
    
    /** 表示カーソル. **/
    protected int cursor = -1 ;
    
    /** オフセット値. **/
    protected int offset = -1 ;
    
    /** リミット値. **/
    protected int limit = -1 ;
    
    /** ゼロ件処理フラグ. **/
    protected boolean zeroFlag = false ;
    
    /** 結果リスト. **/
    protected PointerElement[] result ;
    
    /**
     * コンストラクタ.
     */
    public ResultPointer() {
        
    }
    
    /**
     * 情報生成.
     * @param src データソースを設定します.
     * @param sort ソート条件を設定します.
     * @param off オフセット値を設定します.
     * @param limit リミット値を設定します.
     */
    public void create( MimdbRow[] src,SortElement sort,int off,int limit ) {
        create( null,src,sort,off,limit ) ;
    }
    
    /**
     * 情報生成.
     * @param msearch 対象の検索結果を設定します.
     * @param src データソースを設定します.
     * @param sort ソート条件を設定します.
     * @param off オフセット値を設定します.
     * @param limit リミット値を設定します.
     */
    public void create( MimdbMiddleSearch msearch,MimdbRow[] src,SortElement sort,int off,int limit ) {
        if( sort == null || sort.sortNoList == null ) {
            sort = null ;
        }
        this.msearch = msearch ;
        this.src = src ;
        this.sort = sort ;
        this.offset = off ;
        this.limit = limit ;
        result = null ;
        cursor = -1 ;
    }
    
    /**
     * ゼロ件の情報を生成.
     * @param src データソースを設定します.
     * @param sort ソート条件を設定します.
     * @param off オフセット値を設定します.
     * @param limit リミット値を設定します.
     */
    public void createZero( MimdbRow[] src,SortElement sort,int off,int limit ) {
        create( null,src,sort,off,limit ) ;
        zeroFlag = true ;
    }
    
    /**
     * 情報クリア.
     */
    public void clear() {
        msearch = null ;
        src = null ;
        sort = null ;
        offset = -1 ;
        limit = -1 ;
        result = null ;
        cursor = -1 ;
        zeroFlag = false ;
    }
    
    /**
     * 結果情報を作成.
     * @exception Exception 例外.
     */
    public void execute() throws Exception {
        cursor = -1 ;
        // ゼロ件の情報を作成する場合.
        if( zeroFlag ) {
            result = null ;
            return ;
        }
        // 検索条件が存在する場合.
        if( msearch != null ) {
            result = searchToResult() ;
        }
        // 検索条件が存在しない場合.
        else {
            result = resultNotWhere() ;
        }
        // 情報が取得された場合.
        if( result != null ) {
            cursor = 0 ;
        }
    }
    
    /**
     * 処理結果数を取得.
     * @return int 処理結果数が返却されます.
     */
    public int resultLength() {
        return ( result == null ) ? 0 : result.length ;
    }
    
    /**
     * 指定位置の情報項番を取得.
     * @param no 対象の項番を設定します.
     * @return int 行番号が返却されます.
     */
    public int get( int no ) {
        if( result == null || no < 0 || result.length <= no ) {
            throw new MimdbException( "範囲を超えています:" + no ) ;
        }
        return result[ no ].no ;
    }
    
    /** 指定数のポインター要素配列を作成. **/
    private final PointerElement[] createElement( final int len ) {
        final PointerElement[] ret = new PointerElement[ len ] ;
        for( int i = 0 ; i < len ; i++ ) {
            ret[ i ] = new PointerElement() ;
        }
        return ret ;
    }
    
    /** where句ありでのポインタ情報を作成. **/
    private final PointerElement[] searchToResult() throws Exception {
        
        // 検索結果のデータ長を取得.
        final int length = msearch.size() ;
        if( length == 0 ) {
            return null ;
        }
        
        // 部分取得の場合.
        if( offset >= 0 && limit > 0 ) {
            if( this.offset >= length ) {
                return null ;
            }
            final int off = this.offset ;
            int lmt = this.limit ;
            if( (off&0x3fffffff) + lmt >= length ) {
                lmt = length - (off&0x3fffffff) ;
            }
            
            // ソート条件が存在しない場合.
            if( sort == null || sort.sortNoList == null ) {
                PointerElement[] ret = createElement( lmt ) ;
                msearch.getResultArray( ret,off ) ;
                return ret ;
            }
            
            // ソート条件が存在する場合.
            // 最初にソート専用オブジェクトを生成して、それをソートした後に、
            // オフセット＋リミット範囲の条件を取得.
            PointerElement[] list = createElement( length ) ;
            msearch.getResultArray( list ) ;
            resultSort( list ) ;
            final PointerElement[] ret = new PointerElement[ lmt ] ;
            System.arraycopy( list,off,ret,0,lmt ) ;
            return ret ;
        }
        
        // 全件取得の場合.
        PointerElement[] ret = createElement( length ) ;
        msearch.getResultArray( ret ) ;
        if( sort != null && sort.sortNoList != null ) {
            resultSort( ret ) ;
        }
        return ret ;
    }
    
    /** where句なしでのポインタ情報を作成. **/
    private final PointerElement[] resultNotWhere() throws Exception {
        
        int len = src.length ;
        
        // 部分取得の場合.
        if( offset >= 0 && limit > 0 ) {
            if( this.offset >= len ) {
                return null ;
            }
            final int off = this.offset ;
            int lmt = this.limit ;
            if( (off&0x3fffffff) + lmt >= len ) {
                lmt = len - (off&0x3fffffff) ;
            }
            if( lmt <= 0 ) {
                return null ;
            }
            
            // ソート条件が存在しない場合.
            if( sort == null || sort.sortNoList == null ) {
                final int olLen = off + lmt ;
                final PointerElement[] ret = new PointerElement[ lmt ] ;
                for( int i = off,cnt = 0 ; i < olLen ; i ++ ) {
                    ret[ cnt ++ ] = new PointerElement( i ) ;
                }
                return ret ;
            }
            
            // ソート条件が存在する場合.
            // 最初にソート専用オブジェクトを生成して、それをソートした後に、
            // オフセット＋リミット範囲の条件を取得.
            final PointerElement[] list = new PointerElement[ len ] ;
            for( int i = 0 ; i < len ; i ++ ) {
                list[ i ] = new PointerElement( i ) ;
            }
            resultSort( list ) ;
            final PointerElement[] ret = new PointerElement[ lmt ] ;
            System.arraycopy( list,off,ret,0,lmt ) ;
            return ret ;
        }
        
        // 全件取得の場合.
        PointerElement[] ret = new PointerElement[ len ] ;
        for( int i = 0 ; i < len ; i ++ ) {
            ret[ i ] = new PointerElement( i ) ;
        }
        if( sort != null && sort.sortNoList != null ) {
            resultSort( ret ) ;
        }
        return ret ;
    }
    
    /** ソート処理. **/
    private final void resultSort( final PointerElement[] list ) {
        
        // ソート条件が１つで、インデックスが有効な場合.
        // ただし元の件数およびインデックス数が多すぎる場合は、通常ソートで行う.
        if( sort.indexList.length == 1 && sort.indexList[ 0 ] != null &&
            list.length <= MAX_BACKET_SORT_LENGTH &&
            sort.indexLength <= MAX_BACKET_SORT_LENGTH ) {
            
            // バケットソート.
            backetSort( list ) ;
        }
        else {
            
            // 通常ソート.
            Arrays.sort( list ) ;
        }
    }
    
    /** バケットソート. **/
    private final void backetSort( final PointerElement[] list ) {
        
        int n,i,cnt,len ;
        PointerElement em ;
        ObjectLinkedList<PointerElement> o ;
        
        final int[] idx = sort.indexList[ 0 ] ;
        final ObjectLinkedList<PointerElement>[] array = new ObjectLinkedList[ sort.indexLength + 1 ] ;
        
        // ソート順に、リストセット.
        len = list.length ;
        for( i = 0 ; i < len ; i ++ ) {
            if( ( o = array[ ( n = idx[ ( em = list[ i ] ).no ] + 1 ) ] ) == null ) {
                o = new ObjectLinkedList<PointerElement>() ;
                array[ n ] = o ;
            }
            o.offer( em ) ;
        }
        
        // 降順にセット.
        if( sort.desc[ 0 ] ) {
            cnt = 0 ;
            for( i = array.length-1 ; i >= 0 ; i-- ) {
                if( ( o = array[ i ] ) != null ) {
                    cnt = o.poll( list,cnt ) ;
                }
            }
        }
        // 昇順にセット.
        else {
            cnt = 0 ;
            len = array.length ;
            for( i = 0 ; i < len ; i++ ) {
                if( ( o = array[ i ] ) != null ) {
                    cnt = o.poll( list,cnt ) ;
                }
            }
        }
        
    }
}
