package org.maachang.rimdb.table.array ;

import java.util.Arrays;
import java.util.HashSet;

import org.maachang.rimdb.RimDbException;
import org.maachang.rimdb.index.IndexUtil;
import org.maachang.rimdb.index.SortArray;

/**
 * ユニークID情報.
 * NULL禁止、重複禁止の条件の場合は、インデックス化される.
 * 
 * @version 2014/07/07
 * @author  masahito suzuki
 * @since   rimdb-1.00
 */
@SuppressWarnings("unchecked")
public final class UniqueId {
    
    /** ID情報 **/
    protected final boolean intFlag ;
    protected final long[] longList ;
    protected final int[] intList ;
    protected final int[] lines ;
    
    /**
     * コンストラクタ.
     * @param columns カラム群を設定します.
     */
    public UniqueId( final Integer[] columns ) {
        int i ;
        final int len = columns.length ;
        
        // 全部検索して、重複が無いかチェック
        // 配列型を判別.
        Integer n ;
        HashSet<Integer> set = new HashSet<Integer>() ;
        for( i = 0 ; i < len ; i ++ ) {
            if( ( n = columns[ i ] ) == null ) {
                throw new RimDbException( "[null]を含む内容は、ID配列にはできません" ) ;
            }
            if( set.contains( n ) ) {
                throw new RimDbException( "重複条件を含む内容は、ID配列にはできません" ) ;
            }
            set.add( n ) ;
        }
        set.clear() ;
        set = null ;
        
        // int配列で処理可能な場合.
        SortArray<Integer>[] list = new SortArray[ len ] ;
        for( i = 0 ; i < len ; i ++ ) {
            list[ i ] = new SortArray<Integer>( columns[ i ],i ) ;
        }
        Arrays.sort( list ) ;
        int[] nn = new int[ len ] ;
        int[] ln = new int[ len ] ;
        for( i = 0 ; i < len ; i ++ ) {
            nn[ i ] = list[ i ].n ;
            ln[ i ] = list[ i ].line ;
        }
        intFlag = true ;
        longList = null ;
        intList = nn ;
        lines = ln ;
    }
    
    /**
     * コンストラクタ.
     * @param columns カラム群を設定します.
     */
    public UniqueId( final Long[] columns ) {
        int i ;
        final int len = columns.length ;
        
        // 全部検索して、重複が無いかチェック
        // 配列型を判別.
        Long n ;
        HashSet<Long> set = new HashSet<Long>() ;
        boolean flg = true ;
        int min = Integer.MIN_VALUE ;
        int max = Integer.MAX_VALUE ;
        for( i = 0 ; i < len ; i ++ ) {
            if( ( n = columns[ i ] ) == null ) {
                throw new RimDbException( "[null]を含む内容は、ID配列にはできません" ) ;
            }
            if( set.contains( n ) ) {
                throw new RimDbException( "重複条件を含む内容は、ID配列にはできません" ) ;
            }
            set.add( n ) ;
            if( n < min || n > max ) {
                flg = false ;
            }
        }
        set.clear() ;
        set = null ;
        
        // int配列で処理可能な場合.
        if( flg ) {
            SortArray<Integer>[] list = new SortArray[ len ] ;
            for( i = 0 ; i < len ; i ++ ) {
                list[ i ] = new SortArray<Integer>( columns[ i ].intValue(),i ) ;
            }
            Arrays.sort( list ) ;
            int[] nn = new int[ len ] ;
            int[] ln = new int[ len ] ;
            for( i = 0 ; i < len ; i ++ ) {
                nn[ i ] = list[ i ].n ;
                ln[ i ] = list[ i ].line ;
            }
            intFlag = flg ;
            longList = null ;
            intList = nn ;
            lines = ln ;
        }
        // long配列での処理.
        else {
            SortArray<Long>[] list = new SortArray[ len ] ;
            for( i = 0 ; i < len ; i ++ ) {
                list[ i ] = new SortArray<Long>( columns[ i ],i ) ;
            }
            Arrays.sort( list ) ;
            long[] nn = new long[ len ] ;
            int[] ln = new int[ len ] ;
            for( i = 0 ; i < len ; i ++ ) {
                nn[ i ] = list[ i ].n ;
                ln[ i ] = list[ i ].line ;
            }
            intFlag = flg ;
            longList = nn ;
            intList = null ;
            lines = ln ;
        }
    }
    
    /**
     * 指定位置の情報を取得.
     * @param no 対象の項番を設定します.
     * @return long 対象の情報が返却されます.
     */
    public final long get( final int no ) {
        return intFlag ? (long)intList[ no ] : longList[ no ] ;
    }
    
    /**
     * 指定位置の行番号を取得.
     * @param no 対象の項番を設定します.
     * @return int 行番号が返却されます.
     */
    public final int getLine( final int no ) {
        return lines[ no ] ;
    }
    
    /**
     * 対象情報の行番号を取得.
     * @param id 対象のIDを設定します.
     * @return int 対象の行番号が返却されます.
     *             見つからない場合は[-1]が返却されます.
     */
    public final int search( final int id ) {
        final int no ;
        if( intFlag ) {
            if( ( no = IndexUtil.searchInt( intList,id ) ) == -1 ) {
                return -1 ;
            }
        }
        else {
            if( ( no = IndexUtil.searchLong( longList,(long)id ) ) == -1 ) {
                return -1 ;
            }
        }
        return lines[ no ] ;
    }
    
    /**
     * 対象情報の行番号を取得.
     * @param id 対象のIDを設定します.
     * @return int 対象の行番号が返却されます.
     *             見つからない場合は[-1]が返却されます.
     */
    public final int search( final long id ) {
        final int no ;
        if( intFlag ) {
            if( ( no = IndexUtil.searchInt( intList,(int)id ) ) == -1 ) {
                return -1 ;
            }
        }
        else {
            if( ( no = IndexUtil.searchLong( longList,id ) ) == -1 ) {
                return -1 ;
            }
        }
        return lines[ no ] ;
    }
    
}
