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.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")
public final class RowImpl implements Row {
    
    protected final Columns columns ;
    protected final int[] positionColumns ;
    private final ThreadLocal<int[]> row = new ThreadLocal<int[]>() ;
    
    /**
     * コンストラクタ.
     * @param columns 対象のカラムリストを設定します.
     * @param positionColumns 対象のポジションカラムを設定します.
     */
    protected RowImpl( Columns columns,int[] positionColumns ) {
        this.columns = columns ;
        this.positionColumns = positionColumns ;
    }
    
    /**
     * 情報クリア.
     * 行ポジションがクリアされます.
     */
    public final void clear() {
        row.remove() ;
    }
    
    /**
     * 現在のオブジェクトをコピー.
     * @return Row コピーされたオブジェクトが返却されます.
     */
    public final Row copy() {
        final int[] p = row.get() ;
        if( p == null ) {
            return null ;
        }
        return new CopyRow( columns,positionColumns,p[0] ) ;
    }
    
    /**
     * 行ポジションセット.
     * @param pos 対象のポジションを設定します.
     * @return Row オブジェクトが返却されます.
     */
    public final Row position( final int pos ) {
        int[] p = row.get() ;
        if( p == null ) {
            p = new int[ 1 ] ;
            row.set( p ) ;
        }
        p[ 0 ] = pos ;
        return this ;
    }
    
    /**
     * 行ポジションを取得.
     * @return int ポジションが返却されます.
     */
    public final int position() {
        final int[] p = row.get() ;
        if( p == null ) {
            return -1 ;
        }
        return p[ 0 ] ;
    }
    
    /**
     * カラムタイプを取得.
     * @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( 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( name ) ;
        if( p == -1 ) {
            return null ;
        }
        return columns.list[p].get( position() ) ;
    }
    
    /**
     * 情報取得.
     * @param o 対象の条件を設定します.
     * @return Object オブジェクトが返却されます.
     */
    public final Object get( final Object o ) {
        if( o == null ) {
            return null ;
        }
        else if( o instanceof String ) {
            return getValue( (String)o ) ;
        }
        else if( o instanceof Number ) {
            return getValue( ((Number)o).intValue() ) ;
        }
        else if( ConvertUtil.isNumeric( o ) ) {
            return getValue( ConvertUtil.convertInt( o ) ) ;
        }
        return getValue( ConvertUtil.convertString( o ) ) ;
    }
    
    /**
     * 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) {
        if( key instanceof String ) {
            return columns.getColumnNo( (String)key ) != -1 ;
        }
        else if( key instanceof Number ) {
            int n = ((Number)key).intValue() ;
            return ( n >= 0 && n < columns.length ) ;
        }
        return false ;
    }
    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() ;
    }
}
