package org.maachang.rimdb.table ;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.maachang.rimdb.ColumnMap;
import org.maachang.rimdb.RimDbException;
import org.maachang.rimdb.Row;
import org.maachang.rimdb.index.position.PositionIndex;
import org.maachang.rimdb.table.array.ColumnArray;
import org.maachang.rimdb.util.ConvertUtil;

/**
 * コピー行オブジェクト.
 * 
 * @version 2014/07/07
 * @author  masahito suzuki
 * @since   rimdb-1.00
 */
@SuppressWarnings("unchecked")
final class CopyRow implements Row {
    
    protected final Columns columns ;
    protected final int[] positionColumns ;
    protected final int position ;
    
    /**
     * コンストラクタ.
     * @param impl コピー対象のオブジェクトを設定します.
     */
    protected CopyRow( Columns columns,int[] positionColumns,int position ) {
        this.columns = columns ;
        this.positionColumns = positionColumns ;
        this.position = position ;
    }
    
    /**
     * 情報クリア.
     * 行ポジションがクリアされます.
     */
    public final void clear() {
    }
    
    /**
     * 現在のオブジェクトをコピー.
     * @return Row コピーされたオブジェクトが返却されます.
     */
    public final Row copy() {
        return new CopyRow( columns,positionColumns,position ) ;
    }
    
    /**
     * 現在のオブジェクトをMAP変換.
     * @return ColumnMap 変換されたMapオブジェクトが返却されます.
     * @version rimdb-1.01
     */
    public final ColumnMap toMap() {
        final int len = columns.length ;
        final ColumnMap ret = new ColumnMap( len ) ;
        final String[] names = columns.columnNames ;
        final ColumnArray[] list = columns.list ;
        final int r = position() ;
        
        for( int i = 0 ; i < len ; i ++ ) {
            ret.put( names[ i ],list[ i ].get( r ) ) ;
        }
        return ret ;
    }
    
    /**
     * 現在のオブジェクトをキー名をJava名に変換してMap取得.
     * @return ColumnMap 変換されたMapオブジェクトが返却されます.
     * @version rimdb-1.01
     */
    public final ColumnMap toJavaMap() {
        final int len = columns.length ;
        final ColumnMap ret = new ColumnMap( len ) ;
        final String[] names = columns.columnNames ;
        final ColumnArray[] list = columns.list ;
        final int r = position() ;
        
        for( int i = 0 ; i < len ; i ++ ) {
            ret.put( TableUtil.convertDBNameByJavaName( false,names[ i ] ),list[ i ].get( r ) ) ;
        }
        return ret ;
    }
    
    /**
     * 行ポジションを取得.
     * @return int ポジションが返却されます.
     */
    public final int position() {
        return position ;
    }
    
    /**
     * 行ポジションセット.
     * @param pos 対象のポジションを設定します.
     * @return Row オブジェクトが返却されます.
     */
    public final Row position( final int pos ) {
        throw new RimDbException( "コピーオブジェクトの行位置は変更できません" ) ;
    }
    
    /**
     * カラムタイプを取得.
     * @param no 対象のカラム項番を設定します.
     * @return int カラムタイプが返却されます.
     */
    public final int getType( final int no ) {
        return columns.getType( no ) ;
    }
    
    /**
     * カラムタイプを取得.
     * @param name 対象のカラム名を設定します.
     * @return int カラムタイプが返却されます.
     *             [-1]の場合、取得に失敗しました.
     */
    public final int getType( final String name ) {
        return columns.getType( TableUtil.convertJavaNameByDBName( name ) ) ;
    }
    
    /**
     * カラム数を取得.
     * @return int カラム数が返却されます.
     */
    public final int length() {
        return columns.length ;
    }
    
    /**
     * カラム名を取得.
     * @param no 対象のカラム項番を設定します.
     * @return String カラム名が返却されます.
     */
    public final String getColumnName( final int no ) {
        return columns.columnNames[ no ] ;
    }
    
    /**
     * 情報取得.
     * @param no 対象の項番を設定します.
     * @return Object 情報が返却されます.
     */
    public final Object getValue( final int no ) {
        return columns.list[no].get( position() ) ;
    }
    
    /**
     * 情報取得.
     * @param no 対象の項番を設定します.
     * @return Object 情報が返却されます.
     */
    public final Object getValue( final long no ) {
        return columns.list[(int)no].get( position() ) ;
    }
    
    /**
     * 情報取得.
     * @param name 対象のカラム名を設定します.
     * @return Object 情報が返却されます.
     */
    public final Object getValue( final String name ) {
        if( name == null ) {
            return null ;
        }
        final int p = columns.getColumnNo( TableUtil.convertJavaNameByDBName( name ) ) ;
        if( p == -1 ) {
            return null ;
        }
        return columns.list[p].get( position() ) ;
    }
    
    /** 指定条件から、カラム項番を取得. **/
    private final int _no( Object o ) {
        if( o == null ) {
            return -1 ;
        }
        if( o instanceof String ) {
            String nm = TableUtil.convertJavaNameByDBName( (String)o ) ;
            return columns.getColumnNo( nm ) ;
        }
        else if( o instanceof Number ) {
            int n = ((Number)o).intValue() ;
            if( n < 0 || n >= columns.length ) {
                return -1 ;
            }
            return n ;
        }
        else if( ConvertUtil.isNumeric( o ) ) {
            int n = ConvertUtil.convertInt( o ) ;
            if( n < 0 || n >= columns.length ) {
                return -1 ;
            }
            return n ;
        }
        String nm = TableUtil.convertJavaNameByDBName( ConvertUtil.convertString( o ) ) ;
        return columns.getColumnNo( nm ) ;
    }
    
    /**
     * 情報取得.
     * @param o 対象の条件を設定します.
     * @return Object オブジェクトが返却されます.
     */
    public final Object get( final Object o ) {
        int no = _no( o ) ;
        if( no == -1 ) {
            return null ;
        }
        return columns.list[no].get( position() ) ;
    }
    
    /**
     * boolean情報を取得.
     * @param no 対象の項番を設定します.
     * @return Boolean 情報が返却されます.
     */
    public final Boolean getBoolean( final int no ) {
        return ConvertUtil.convertBool( getValue( no ) ) ;
    }
    
    /**
     * int情報を取得.
     * @param no 対象の項番を設定します.
     * @return Integer 情報が返却されます.
     */
    public final Integer getInt( final int no ) {
        return ConvertUtil.convertInt( getValue( no ) ) ;
    }
    
    /**
     * long情報を取得.
     * @param no 対象の項番を設定します.
     * @return Long 情報が返却されます.
     */
    public final Long getLong( final int no ) {
        return ConvertUtil.convertLong( getValue( no ) ) ;
    }
    
    /**
     * float情報を取得.
     * @param no 対象の項番を設定します.
     * @return Float 情報が返却されます.
     */
    public final Float getFloat( final int no ) {
        return ConvertUtil.convertFloat( getValue( no ) ) ;
    }
    
    /**
     * double情報を取得.
     * @param no 対象の項番を設定します.
     * @return Double 情報が返却されます.
     */
    public final Double getDouble( final int no ) {
        return ConvertUtil.convertDouble( getValue( no ) ) ;
    }
    
    /**
     * String情報を取得.
     * @param no 対象の項番を設定します.
     * @return String 情報が返却されます.
     */
    public final String getString( final int no ) {
        return ConvertUtil.convertString( getValue( no ) ) ;
    }
    
    /**
     * Date情報を取得.
     * @param no 対象の項番を設定します.
     * @return Date 情報が返却されます.
     */
    public final java.sql.Date getDate( final int no ) {
        return ConvertUtil.convertSqlDate( getValue( no ) ) ;
    }
    
    /**
     * Time情報を取得.
     * @param no 対象の項番を設定します.
     * @return Time 情報が返却されます.
     */
    public final java.sql.Time getTime( final int no ) {
        return ConvertUtil.convertSqlTime( getValue( no ) ) ;
    }
    
    /**
     * Timestamp情報を取得.
     * @param no 対象の項番を設定します.
     * @return Timestamp 情報が返却されます.
     */
    public final java.sql.Timestamp getTimestamp( final int no ) {
        return ConvertUtil.convertSqlTimestamp( getValue( no ) ) ;
    }
    
    /**
     * boolean情報を取得.
     * @param no 対象の項番を設定します.
     * @return Boolean 情報が返却されます.
     */
    public final Boolean getBoolean( final long no ) {
        return ConvertUtil.convertBool( getValue( no ) ) ;
    }
    
    /**
     * int情報を取得.
     * @param no 対象の項番を設定します.
     * @return Integer 情報が返却されます.
     */
    public final Integer getInt( final long no ) {
        return ConvertUtil.convertInt( getValue( no ) ) ;
    }
    
    /**
     * long情報を取得.
     * @param no 対象の項番を設定します.
     * @return Long 情報が返却されます.
     */
    public final Long getLong( final long no ) {
        return ConvertUtil.convertLong( getValue( no ) ) ;
    }
    
    /**
     * float情報を取得.
     * @param no 対象の項番を設定します.
     * @return Float 情報が返却されます.
     */
    public final Float getFloat( final long no ) {
        return ConvertUtil.convertFloat( getValue( no ) ) ;
    }
    
    /**
     * double情報を取得.
     * @param no 対象の項番を設定します.
     * @return Double 情報が返却されます.
     */
    public final Double getDouble( final long no ) {
        return ConvertUtil.convertDouble( getValue( no ) ) ;
    }
    
    /**
     * String情報を取得.
     * @param no 対象の項番を設定します.
     * @return String 情報が返却されます.
     */
    public final String getString( final long no ) {
        return ConvertUtil.convertString( getValue( no ) ) ;
    }
    
    /**
     * Date情報を取得.
     * @param no 対象の項番を設定します.
     * @return Date 情報が返却されます.
     */
    public final java.sql.Date getDate( final long no ) {
        return ConvertUtil.convertSqlDate( getValue( no ) ) ;
    }
    
    /**
     * Time情報を取得.
     * @param no 対象の項番を設定します.
     * @return Time 情報が返却されます.
     */
    public final java.sql.Time getTime( final long no ) {
        return ConvertUtil.convertSqlTime( getValue( no ) ) ;
    }
    
    /**
     * Timestamp情報を取得.
     * @param no 対象の項番を設定します.
     * @return Timestamp 情報が返却されます.
     */
    public final java.sql.Timestamp getTimestamp( final long no ) {
        return ConvertUtil.convertSqlTimestamp( getValue( no ) ) ;
    }
    
    /**
     * boolean情報を取得.
     * @param key 対象のカラム名を設定します.
     * @return Boolean 情報が返却されます.
     */
    public final Boolean getBoolean( final String key ) {
        return ConvertUtil.convertBool( getValue( key ) ) ;
    }
    
    /**
     * int情報を取得.
     * @param key 対象のカラム名を設定します.
     * @return Integer 情報が返却されます.
     */
    public final Integer getInt( final String key ) {
        return ConvertUtil.convertInt( getValue( key ) ) ;
    }
    
    /**
     * long情報を取得.
     * @param key 対象のカラム名を設定します.
     * @return Long 情報が返却されます.
     */
    public final  Long getLong( final String key ) {
        return ConvertUtil.convertLong( getValue( key ) ) ;
    }
    
    /**
     * float情報を取得.
     * @param key 対象のカラム名を設定します.
     * @return Float 情報が返却されます.
     */
    public final Float getFloat( final String key ) {
        return ConvertUtil.convertFloat( getValue( key ) ) ;
    }
    
    /**
     * double情報を取得.
     * @param key 対象のカラム名を設定します.
     * @return Double 情報が返却されます.
     */
    public final Double getDouble( final String key ) {
        return ConvertUtil.convertDouble( getValue( key ) ) ;
    }
    
    /**
     * String情報を取得.
     * @param key 対象のカラム名を設定します.
     * @return String 情報が返却されます.
     */
    public final String getString( final String key ) {
        return ConvertUtil.convertString( getValue( key ) ) ;
    }
    
    /**
     * Date情報を取得.
     * @param key 対象のカラム名を設定します.
     * @return Date 情報が返却されます.
     */
    public final java.sql.Date getDate( final String key ) {
        return ConvertUtil.convertSqlDate( getValue( key ) ) ;
    }
    
    /**
     * Time情報を取得.
     * @param key 対象のカラム名を設定します.
     * @return Time 情報が返却されます.
     */
    public final java.sql.Time getTime( final String key ) {
        return ConvertUtil.convertSqlTime( getValue( key ) ) ;
    }
    
    /**
     * Timestamp情報を取得.
     * @param key 対象のカラム名を設定します.
     * @return Timestamp 情報が返却されます.
     */
    public final java.sql.Timestamp getTimestamp( final String key ) {
        return ConvertUtil.convertSqlTimestamp( getValue( key ) ) ;
    }
    
    /**
     * 空間インデックスの距離を取得.
     * @param x 中心点X軸を設定します.
     * @param y 中心点Y軸を設定します.
     * @return int 距離が返却されます.
     */
    public final int getPoint( final int x,final int y ) {
        if( positionColumns == null ) {
            throw new RimDbException( "空間インデックスは利用できません" ) ;
        }
        return PositionIndex.getLine( x,y,
            (Integer)columns.list[positionColumns[0]].get( position() ),
            (Integer)columns.list[positionColumns[1]].get( position() ) ) ;
    }
    
    /**
     * 空間インデックスの距離を取得.
     * @param lat 中心緯度を設定します.
     * @param lon 中心経度を設定します.
     * @return int 距離が返却されます.
     */
    public final int getPoint( final Number lat,final Number lon ) {
        if( positionColumns == null ) {
            throw new RimDbException( "空間インデックスは利用できません" ) ;
        }
        int x,y ;
        if( lat instanceof Double || lat instanceof Float || lat instanceof BigDecimal ) {
            x = PositionIndex.getLat( lat.doubleValue() ) ;
        }
        else {
            x = lat.intValue() ;
        }
        if( lon instanceof Double || lon instanceof Float || lat instanceof BigDecimal ) {
            y = PositionIndex.getLon( lon.doubleValue() ) ;
        }
        else {
            y = lon.intValue() ;
        }
        return PositionIndex.getLine( x,y,
            (Integer)columns.list[positionColumns[0]].get( position() ),
            (Integer)columns.list[positionColumns[1]].get( position() ) ) ;
    }
    
    /*********************** 以下 Map定義 ***********************/
    
    public final void putAll(final Map toMerge) {
        // 処理なし.
    }
    public final boolean containsValue(final Object value) {
        final int len = columns.length ;
        final ColumnArray[] list = columns.list ;
        final int r = position() ;
        if( value == null ) {
            for( int i = 0 ; i < len ; i ++ ) {
                if( list[ i ].get( r ) == null ) {
                    return true ;
                }
            }
        }
        else {
            for( int i = 0 ; i < len ; i ++ ) {
                if( value.equals( list[ i ].get( r ) ) ) {
                    return true ;
                }
            }
        }
        return false ;
    }
    public final Set entrySet() {
        return null ;
    }
    public final Collection values() {
        final int len = columns.length ;
        final ColumnArray[] list = columns.list ;
        final ArrayList ret = new ArrayList() ;
        final int r = position() ;
        for( int i = 0 ; i < len ; i ++ ) {
            ret.add( list[ i ].get( r ) ) ;
        }
        return ret ;
    }
    public final Object put(final Object name, final Object value) {
        return null ;
    }
    public final boolean containsKey(final Object key) {
        return _no( key ) != -1 ;
    }
    public final Object remove(final Object key) {
        return null ;
    }
    public final boolean isEmpty() {
        return false ;
    }
    public final Set keySet() {
        Set<Object> ret = new HashSet<Object>() ;
        getAllKey( ret ) ;
        return ret ;
    }
    public int size() {
        return columns.length ;
    }
    public final void getAllKey( final Set<Object> set ) {
        final int len = columns.length ;
        final String[] names = columns.columnNames ;
        for( int i = 0 ; i < len ; i ++ ) {
            set.add( names[ i ] ) ;
        }
    }
    public final void getAllValues( final Set<Object> set ) {
        final int len = columns.length ;
        final ColumnArray[] list = columns.list ;
        final int r = position() ;
        for( int i = 0 ; i < len ; i ++ ) {
            set.add( list[ i ].get( r ) ) ;
        }
    }
    public final String toString() {
        Object v ;
        final int len = columns.length ;
        final String[] names = columns.columnNames ;
        final StringBuilder buf = new StringBuilder() ;
        final ColumnArray[] list = columns.list ;
        final int r = position() ;
        
        buf.append( "{" ) ;
        for( int i = 0 ; i < len ; i ++ ) {
            if( i != 0 ) {
                buf.append( "," ) ;
            }
            v = list[ i ].get( r ) ;
            if( v instanceof String ) {
                buf.append( names[ i ] ).append( ":\"" ).append( v ).append( "\"" ) ;
            }
            else {
                buf.append( names[ i ] ).append( ":" ).append( v ) ;
            }
        }
        return buf.append( "}" ).toString() ;
    }
}
