package org.maachang.rimdb.index.position ;

import java.util.Arrays;

import org.maachang.rimdb.index.Mask;
import org.maachang.rimdb.index.position.PositionIndex.YLineIndex;
import org.maachang.rimdb.table.array.IntegerArray;
import org.maachang.rimdb.util.NList;
import org.maachang.rimdb.util.NOKeyValue;

/**
 * 空間インデックス作成オブジェクト.
 * 
 * @version 2014/07/07
 * @author  masahito suzuki
 * @since   rimdb-1.00
 */
@SuppressWarnings("unchecked")
public final class CreatePositionIndex {
    
    /** Y軸インデックス作成用. **/
    private static final class SrcYIndex implements Comparable {
        Integer y ;
        final NList lines = new NList() ;
        public int compareTo( Object n ) {
            return y.compareTo( ((SrcYIndex)n).y ) ;
        }
    }
    
    /** X軸インデックス作成用. **/
    private static final class SrcXIndex implements Comparable {
        Integer x ;
        NOKeyValue<SrcYIndex> yList = new NOKeyValue<SrcYIndex>() ;
        public int compareTo( Object n ) {
            return x.compareTo( ((SrcXIndex)n).x ) ;
        }
    }
    
    /** 精度. **/
    protected int accuracy ;
    
    /** X軸、Y軸情報. **/
    protected int[] xArray ;
    protected int[] yArray ;
    
    /**
     * コンストラクタ.
     * @param accuracy 精度を設定します.
     */
    public CreatePositionIndex( int accuracy,IntegerArray xArray,IntegerArray yArray ) {
        if( accuracy <= 0 ) {
            accuracy = 1 ;
        }
        this.accuracy = accuracy ;
        this.xArray = xArray.getArray() ;
        this.yArray = yArray.getArray() ;
    }
    
    /**
     * インデックス情報を生成.
     * @return PositionIndex 生成されたインデックスオブジェクトが返却されます.
     */
    public PositionIndex create() {
        PositionIndex ret = _create( accuracy,xArray,yArray ) ;
        xArray = null ;
        yArray = null ;
        return ret ;
    }
    
    /** インデックス作成. */
    private static final PositionIndex _create( final int accuracy,final int[] xList,final int[] yList ) {
        int i,x,y ;
        SrcXIndex sx ;
        SrcYIndex sy ;
        int len = xList.length ;
        
        // X軸を中心に、Y軸をセット.
        NOKeyValue<SrcXIndex> idx = new NOKeyValue<SrcXIndex>() ;
        for( i = 0 ; i < len ; i ++ ) {
            
            // 精度を元に変換.
            x = xList[ i ] / accuracy ;
            y = yList[ i ] / accuracy ;
            if( ( sx = idx.get( x ) ) == null ) {
                sx = new SrcXIndex() ;
                sx.x = x ;
                idx.put( x,sx ) ;
            }
            if( ( sy = sx.yList.get( y ) ) == null ) {
                sy = new SrcYIndex() ;
                sy.y = y ;
                sx.yList.put( y,sy ) ;
            }
            sy.lines.add( i ) ;
        }
        
        // X軸をリスト化.
        len = idx.size() ;
        SrcXIndex[] list = new SrcXIndex[ len ] ;
        idx.reset() ;
        i = 0 ;
        while( idx.hasNext() ) {
            list[ i ++ ] = idx.nextValue() ;
        }
        idx.clear() ;
        idx = null ;
        
        // X軸をソート.
        Arrays.sort( list ) ;
        len = list.length ;
        
        // インデックス情報を作成.
        int[] xIndex = new int[ len ] ;
        int[][] yIndex = new int[ len ][] ;
        YLineIndex[][] yLineIndex = new YLineIndex[ len ][] ;
        
        // X軸に所属する、Y軸のインデックス作成.
        int j,lenJ ;
        NOKeyValue<SrcYIndex> yidx ;
        SrcYIndex[] ylist ;
        for( i = 0 ; i < len ; i ++ ) {
            
            yidx = list[ i ].yList ;
            lenJ = yidx.size() ;
            ylist = new SrcYIndex[ lenJ ] ;
            j = 0 ;
            yidx.reset() ;
            while( yidx.hasNext() ) {
                ylist[ j ++ ] = yidx.nextValue() ;
            }
            yidx.clear() ;
            yidx = null ;
            list[ i ].yList = null ;
            
            // Y軸をソート.
            Arrays.sort( ylist ) ;
            
            // Xインデックス情報を作成.
            xIndex[ i ] = list[ i ].x ;
            list[ i ].x = null ;
            list[ i ] = null ;
            
            // Yインデックス情報を作成.
            createYIndex( yIndex,yLineIndex,i,ylist ) ;
            ylist = null ;
        }
        
        return new PositionIndex( accuracy,xList,yList,xIndex,yIndex,yLineIndex ) ;
    }
    
    /** Yインデックスを作成. **/
    private static final void createYIndex( final int[][] outY,final YLineIndex[][] outYLine,final int no,final SrcYIndex[] yIndex ) {
        final int len = yIndex.length ;
        final int[] yPosIndex = new int[ len ] ;
        final YLineIndex[] yLineIndex = new YLineIndex[ len ] ;
        
        outY[ no ] = yPosIndex ;
        outYLine[ no ] = yLineIndex ;
        
        int[] yLine ;
        int[] yMask ;
        SrcYIndex y ;
        NList list ;
        Mask msk ;
        NOKeyValue<Mask> maskList ;
        YLineIndex yline ;
        
        int j,lenJ,n ;
        maskList = new NOKeyValue<Mask>() ;
        for( int i = 0 ; i < len ; i ++ ) {
            
            yPosIndex[ i ] = ( y = yIndex[ i ] ).y ;
            yIndex[ i ] = null ;
            lenJ = ( list = y.lines ).size() ;
            for( j = 0 ; j < lenJ ; j ++ ) {
                if( ( msk = maskList.get( ( n = list.get( j ) ) >> 5 ) ) == null ) {
                    maskList.put( n >> 5,new Mask( 1 << ( n & 31 ) ) ) ;
                }
                else {
                    msk.value |= 1 << ( n & 31 ) ;
                }
            }
            msk = null ;
            list = null ;
            
            lenJ = maskList.size() ;
            yLine = new int[ lenJ ] ;
            yMask = new int[ lenJ ] ;
            
            maskList.reset() ;
            j = 0 ;
            while( maskList.hasNext() ) {
                yLine[ j ] = maskList.next() ;
                yMask[ j++ ] = maskList.nextValue().value ;
            }
            maskList.clear() ;
            yline = new YLineIndex() ;
            yLineIndex[ i ] = yline ;
            yline.yLine = yLine ;
            yline.yMask = yMask ;
            yLine = null ;
            yMask = null ;
            yline = null ;
            
        }
    }
}
