package org.maachang.rimdb ;

import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import org.maachang.rimdb.table.TableUtil;
import org.maachang.rimdb.util.ConvertUtil;

/**
 * ColumnMap.
 * 
 * @version 2014/09/09
 * @author masahito suzuki
 * @since   rimdb-1.01
 */
@SuppressWarnings("unchecked")
public class ColumnMap  implements Map<String,Object> {
    private static final int DEF_LENGTH = 32 ;
    private static final int MIN_LENGTH = 4 ;
    
    /** 要素情報. **/
    private static final class ColumnMapChild {
        public Object b ;
        public Object o ;
        ColumnMapChild n ;
        ColumnMapChild( Object k,Object v ) {
            b = k ; o = v ;
        }
    }
    
    private ColumnMapChild[] list ;
    private int mask ;
    private int length ;
    private int limit ;
    private int base ;
    
    private boolean nullFlag ;
    private Object nullValue ;
    
    /**
     * コンストラクタ.
     */
    public ColumnMap() {
        this( DEF_LENGTH ) ;
    }
    
    /**
     * コンストラクタ.
     * @param size 初期サイズを設定します.この条件は２の倍数である必要があります.
     */
    public ColumnMap( int size ) {
        if( size < MIN_LENGTH ) {
            size = MIN_LENGTH ;
        }
        else {
            size = ConvertUtil.bitMask( size ) ;
        }
        list = new ColumnMapChild[ size ] ;
        length = 0 ;
        mask = size - 1 ;
        limit = size ;
        base = size ;
        nullFlag = false ;
        nullValue = null ;
    }
    
    /**
     * クリア.
     */
    public void clear() {
        if( length != 0 || list == null || list.length != base ) {
            list = new ColumnMapChild[ base ] ;
        }
        length = 0 ;
        mask = base - 1 ;
        limit = base ;
        nullFlag = false ;
        nullValue = null ;
    }
    
    /**
     * 現在のオブジェクトをキー名をJava名に変換してMap取得.
     * @return ColumnMap 変換されたMapオブジェクトが返却されます.
     */
    public final ColumnMap toJavaMap() {
        final ColumnMap ret = new ColumnMap( size() ) ;
        reset() ;
        while( hasNext() ) {
            ret.put( TableUtil.convertDBNameByJavaName( false,next() ),nextValue() ) ;
        }
        return ret ;
    }
    
    /**
     * 現在のオブジェクトをキー名をSQL名に変換してMap取得.
     * @return ColumnMap 変換されたMapオブジェクトが返却されます.
     */
    public final ColumnMap toSqlMap() {
        final ColumnMap ret = new ColumnMap( size() ) ;
        reset() ;
        while( hasNext() ) {
            ret.put( TableUtil.convertJavaNameByDBName( next() ),nextValue() ) ;
        }
        return ret ;
    }
    
    /**
     * 数値追加.
     * @param b 対象の数値を設定します.
     * @param o 対象の要素を設定します.
     * @return Object 前回の要素が返却されます.
     */
    public Object put( String b,Object o ) {
        if( b == null ) {
            nullFlag = true ;
            nullValue = o ;
            return null ;
        }
        int h,bh ;
        Object ret = null ;
        if( length + 1 >= limit ) {
            int nLen = limit << 1 ;
            int msk = nLen - 1 ;
            ColumnMapChild[] nList = new ColumnMapChild[ nLen ] ;
            ColumnMapChild n,t ;
            for( int i = 0 ; i < limit ; i ++ ) {
                n = list[ i ] ;
                while( n != null ) {
                    if( nList[ ( h = n.b.hashCode() & msk ) ] == null ) {
                        t = n.n ;
                        n.n = null ;
                    }
                    else {
                        t = n.n ;
                        n.n = nList[ h ] ;
                    }
                    nList[ h ] = n ;
                    n = t ;
                }
            }
            list = nList ;
            limit = nLen ;
            mask = msk ;
        }
        if( list[ ( h = b.hashCode() & mask ) ] == null ) {
            list[ h ] = new ColumnMapChild( b,o ) ;
            length ++ ;
        }
        else {
            bh = b.hashCode() ;
            ColumnMapChild nn = list[ h ] ;
            while( nn.n != null ) {
                if( nn.b.hashCode() == bh && ( nn.b == b || nn.b.equals( b ) ) ) {
                    ret = nn.o ;
                    nn.o = o ;
                    return ret ;
                }
                nn = nn.n ;
            }
            if( nn.b.hashCode() == bh && ( nn.b == b || nn.b.equals( b ) ) ) {
                ret = nn.o ;
                nn.o = o ;
            }
            else {
                nn.n = new ColumnMapChild( b,o ) ;
                length ++ ;
            }
        }
        return ret ;
    }
    
    /**
     * 情報が存在するかチェック.
     * @param b 対象のキーを設定します.
     * @return boolean [true]の場合、数値は存在します.
     */
    public boolean containsKey( Object b ) {
        if( b == null ) {
            return nullFlag ;
        }
        int hb = b.hashCode() ;
        ColumnMapChild n = list[ hb & mask ] ;
        while( n != null ) {
            if( n.b.hashCode() == hb && ( n.b == b || n.b.equals( b ) ) ) {
                return true ;
            }
            n = n.n ;
        }
        return false ;
    }
    
    /**
     * 情報が存在するかチェック.
     * @param b 対象のキーを設定します.
     * @return Object 要素情報が返却されます.
     */
    public Object get( Object b ) {
        if( b == null ) {
            return nullValue ;
        }
        int hb = b.hashCode() ;
        ColumnMapChild n = list[ hb & mask ] ;
        while( n != null ) {
            if( n.b.hashCode() == hb && ( n.b == b || n.b.equals( b ) ) ) {
                return n.o ;
            }
            n = n.n ;
        }
        return null ;
    }
    
    /**
     * 削除処理.
     * @param b 対象のキーを設定します.
     * @return Object 削除された要素が返却されます.
     */
    public Object remove( Object b ) {
        if( b == null ) {
            nullFlag = false ;
            Object ret = nullValue ;
            nullValue = null ;
            return ret ;
        }
        Object ret = null ;
        int bh = b.hashCode() ;
        ColumnMapChild bf = null ;
        ColumnMapChild n = list[ bh & mask ] ;
        while( n != null ) {
            if( n.b.hashCode() == bh && ( n.b == b || n.b.equals( b ) ) ) {
                if( bf == null ) {
                    if( n.n == null ) {
                        ret = list[ bh & mask ].o ;
                        list[ bh & mask ] = null ;
                    }
                    else {
                        ret = list[ bh & mask ].o ;
                        list[ bh & mask ] = n.n ;
                    }
                }
                else {
                    if( n.n == null ) {
                        ret = bf.o ;
                        bf.n = null ;
                    }
                    else {
                        ret = bf.o ;
                        bf.n = n.n ;
                    }
                }
                length -- ;
                break ;
            }
            bf = n ;
            n = n.n ;
        }
        return ret ;
    }
    
    /**
     * サイズを取得.
     * @return int サイズが返却されます.
     */
    public int size() {
        return ( nullFlag ) ? length + 1 : length ;
    }
    
    /**
     * 配列として返却.
     * @return Object[] 配列として返却します.
     */
    public Object[] keyArray() {
        if( size() == 0 ) {
            return null ;
        }
        ColumnMapChild n ;
        int cnt ;
        Object[] ret ;
        if( nullFlag ) {
            ret = new Object[ length + 1 ] ;
            cnt = 1 ;
        }
        else {
            ret = new Object[ length ] ;
            cnt = 0 ;
        }
        for( int i = 0 ; i < limit ; i ++ ) {
            if( list[ i ] != null ) {
                n = list[ i ] ;
                while( n != null ) {
                    ret[ cnt ++ ] = n.b ;
                    n = n.n ;
                }
            }
        }
        return ret ;
    }
    
    /** 位置読み込み条件. **/
    private int pos = 0 ;
    private ColumnMapChild cPos = null ;
    private int count = 0 ;
    private boolean nullOutFlag = false ;
    
    /**
     * 読み込み中の位置をリセット.
     * @return ColumnMap このオブジェクトが返却されます.
     */
    public ColumnMap reset() {
        pos = 0 ;
        cPos = null ;
        count = 0 ;
        nullOutFlag = false ;
        return this ;
    }
    
    /**
     * 次の読み込み条件を取得.
     * @return boolean [true]が返却された場合、情報は存在します.
     */
    public boolean hasNext() {
        if( count == 0 ) {
            if( nullFlag ) {
                count ++ ;
                return true ;
            }
            else {
                nullOutFlag = true ;
            }
        }
        if( cPos != null ) {
            if( ( cPos = cPos.n ) != null ) {
                count ++ ;
                return true ;
            }
        }
        while( pos < limit ) {
            if( ( cPos = list[ pos ++ ] ) != null ) {
                count ++ ;
                return true ;
            }
        }
        return false ;
    }
    
    /**
     * 現在の読み込み位置の情報を取得.
     * @return String 現在の読み込み位置の内容が返却されます.
     *                 [null]の場合は、情報は存在しません.
     */
    public String next() {
        if( !nullOutFlag ) {
            nullOutFlag = true ;
            return null ;
        }
        if( cPos == null ) {
            return null ;
        }
        return (String)cPos.b ;
    }
    
    /*
     * 現在の読み込み位置の要素情報を取得.
     * @return Object 現在の読み込み位置の内容が返却されます.
     */
    public Object nextValue() {
        if( cPos == null ) {
            return null ;
        }
        return cPos.o ;
    }
    
    /**
     * カウントを取得.
     * @return int 現在読み込み中の位置が返却されます.
     */
    public int count() {
        return count ;
    }
    
    /**
     * Iteratorを取得.
     * @return Iterator<String> が返却されます.
     */
    public Iterator<String> iterator() {
        OKV_Iterator ret = new OKV_Iterator() ;
        ret._this = this ;
        reset() ;
        return ret ;
    }
    
    /**
     * 文字列変換.
     * @return String 文字列情報が返却されます.
     */
    public String toString() {
        /*
        try {
            return Json.encodeJSON( this ) ;
        } catch( Exception e ) {
            return null ;
        }
        */
        reset() ;
        boolean flg = false ;
        StringBuilder buf = new StringBuilder() ;
        buf.append( "{" ) ;
        while( hasNext() ) {
            next() ;
            if( flg ) {
                buf.append( "," ) ;
            }
            buf.append( cPos.b ) ;
            buf.append( ":" ) ;
            if( cPos.o instanceof String ) {
                buf.append( "\"" ).append( cPos.o ).append( "\"" ) ;
            }
            else {
                buf.append( cPos.o ) ;
            }
            flg = true ;
        }
        buf.append( "}" ) ;
        return buf.toString() ;
    }
    
    ////////////////////////////////////////////////////////////////////////////
    // 以下Map定義.
    ////////////////////////////////////////////////////////////////////////////
    
    public void putAll(Map<? extends String, ? extends Object> toMerge) {
        String s ;
        Iterator it = toMerge.keySet().iterator() ;
        while( it.hasNext() ) {
            put( ( s = (String)it.next() ),toMerge.get( s ) ) ;
        }
    }
    public boolean containsValue(Object value) {
        return false ;
    }
    public Set entrySet() {
        return null ;
    }
    public Collection<Object> values() {
        return null ;
    }
    public boolean isEmpty() {
        return size() == 0 ;
    }
    public Set<String> keySet() {
        OKV_KeySet ret = new OKV_KeySet() ;
        ret._this = this ;
        return ret ;
    }
    public void getAllKey( Set<Object> set ) {
    }
    public void getAllValues( Set<Object> set ) {
    }
    
    /** KeySet実装. **/
    private final class OKV_KeySet implements Set<String> {
        ColumnMap _this ;
        public boolean add(String e) { return false ; }
        public boolean addAll(Collection<? extends String> c) { return false ; }
        public void clear() {}
        public boolean contains(Object o) {
            return _this.containsKey( (String)o ) ;
        }
        public boolean containsAll(Collection<?> c) { return false ; }
        public boolean equals(Object o) { return false ; }
        public int hashCode() { return -1 ; }
        public boolean isEmpty() { return _this.size() == 0 ; }
        public Iterator<String> iterator() {
            return _this.iterator() ;
        }
        public boolean remove(Object o) { return false ; }
        public boolean removeAll(Collection<?> c) { return false ; }
        public boolean retainAll(Collection<?> c) { return false ; }
        public int size() {
            return size() ;
        }
        public Object[] toArray() {
            return _this.keyArray() ;
        }
        public <T> T[] toArray(T[] a) { return null ; }
    }
    
    /** Iterator実装. **/
    private final class OKV_Iterator implements Iterator<String> {
        ColumnMap _this ;
        public boolean hasNext() {
            return _this.hasNext() ;
        }
        public String next() {
            return _this.next() ;
        }
        public void remove() {
        }
    }
    
}
