package org.maachang.leveldb ;

import java.util.*;

/**
 * Leveldb-WriteBatchのMap実装.
 * 
 * @version 2014/09/16
 * @author  masahito suzuki
 * @since   leveldb-1.01
 */
@SuppressWarnings("unchecked")
public class WriteBatchMap implements Map<String,Object> {
    
    /** 元のLeveldbMap. **/
    protected LevelMap src = null ;
    
    /** WriteBatchオブジェクト. **/
    protected WriteBatch batch = null ;
    
    /** コンストラクタ. **/
    protected WriteBatchMap() {}
    
    /**
     * コンストラクタ.
     * @param map 対象のLevelMapを設定します.
     */
    protected WriteBatchMap( LevelMap map ) {
        src = map ;
    }
    
    /**
     * デストラクタ.
     */
    protected final void finalize() throws Exception {
        clear() ;
    }
    
    /**
     * キャッシュクリア.
     * ThreadLocalで管理しているキャッシュ情報をクリアします.
     */
    public final void clearCache() {
        src.clearCache() ;
    }
    
    /**
     * 情報クリア.
     * <p>この処理を呼び出すことで、今まで書き込んでいた情報はキャンセルされます.</p>
     */
    public final void clear() {
        if( batch != null ) {
            batch.close() ;
            batch = null ;
        }
    }
    
    /**
     * 書き込み処理.
     * <p>この処理を呼び出すことで、今まで書き込んでいた情報が反映されます.</p>
     */
    public final void write() {
        if( batch != null ) {
            try {
                batch.write( src.leveldb ) ;
            } finally {
                clear() ;
            }
        }
    }
    
    /** WriteBatchが存在しない場合は生成. **/
    protected final void writeBatch() {
        if( batch == null ) {
            batch = new WriteBatch() ;
        }
    }
    
    /**
     * 指定Map情報の内容をすべてセット.
     * @param toMerge 追加対象のMapを設定します.
     */
    public final void putAll(Map toMerge) {
        Object k ;
        if( toMerge.size() > 0 ) {
            Iterator it = toMerge.keySet().iterator() ;
            while( it.hasNext() ) {
                k = it.next() ;
                if( k instanceof String ) {
                    put( (String)k,toMerge.get( k ) ) ;
                }
            }
        }
    }
    
    /**
     * 指定要素が存在するかチェック.
     * ※Iteratorでチェックするので、件数が多い場合は、処理に時間がかかります.
     * @param value 対象のValueを設定します.
     * @return boolean trueの場合、一致する条件が存在します.
     */
    public final boolean containsValue(Object value) {
        return src.containsValue( value ) ;
    }
    
    /**
     * この処理はLeveMapでは何もしません.
     * return 例外が返却されます.
     */
    public Set entrySet() {
        return src.entrySet() ;
    }
    
    /**
     * この処理はLeveMapでは何もしません.
     * return 例外が返却されます.
     */
    public Collection values() {
        return src.values() ;
    }
    
    /**
     * 指定キーの情報をセット.
     * @param name 対象のキーを設定します.
     * @param value 対象の要素を設定します.
     *              この条件は、数値、文字列、日付系(java.util.Date),配列、
     *              List、Map、Set、Serializableオブジェクト以外をセットすると、
     *              エラーととなります.
     * @return Object [null]が返却されます.
     */
    public final Object put(String name, Object value) {
        if( name == null ) {
            return null ;
        }
        else if( value != null && value instanceof LevelMap ) {
            throw new LeveldbException( "要素にLevelMap要素は設定できません" ) ;
        }
        writeBatch() ;
        try {
            JniBuffer keyBuf = LevelMap.getKeyBuffer( name ) ;
            JniBuffer valBuf = LevelMap.getValueBuffer( value ) ;
            batch.put( keyBuf,valBuf ) ;
            keyBuf.clear() ; valBuf.clear() ;
            return null ;
        } catch( LeveldbException le ) {
            throw le ;
        } catch( Exception e ) {
            throw new LeveldbException( e ) ;
        }
    }
    
    /**
     * 指定キー情報が存在するかチェック.
     * @param key 対象のキーを設定します.
     * @return boolean [true]の場合、存在します.
     */
    public final boolean containsKey(Object key) {
        return src.containsKey( key ) ;
    }
    
    /**
     * 指定キー情報に対する要素を取得.
     * @param key 対象のキーを設定します.
     * @return Object 対象の要素が返却されます.
     */
    public final Object get(Object key) {
        return src.get( key ) ;
    }
    
    /**
     * 指定キーの情報を削除.
     * @param key 対象のキーを設定します.
     * @return Object 削除できた場合[true]が返却されます.
     */
    public final Object remove(Object key) {
        if( key == null ) {
            return false ;
        }
        writeBatch() ;
        try {
            JniBuffer keyBuf = LevelMap.getKeyBuffer( (String)key ) ;
            batch.remove( keyBuf ) ;
            keyBuf.clear() ;
            return null ;
        } catch( LeveldbException le ) {
            throw le ;
        } catch( Exception e ) {
            throw new LeveldbException( e ) ;
        }
    }
    
    /**
     * 情報が空かチェック.
     * return boolean [false]が返却されます.
     */
    public final boolean isEmpty() {
        return src.isEmpty() ;
    }
    
    /**
     * Setオブジェクトを取得.
     * @return Set Setオブジェクトが返却されます.
     */
    public final Set keySet() {
        return src.keySet() ;
    }
    
    /**
     * 登録データ数を取得.
     * ※Iteratorでカウントするので、件数が多い場合は、処理に時間がかかります.
     * return int 登録データ数が返却されます.
     */
    public final int size() {
        return src.size() ;
    }
    
    /**
     * この処理はLeveMapでは何もしません.
     * @param set 例外が発生します.
     */
    public final void getAllKey( Set<Object> set ) {
        src.getAllKey( set ) ;
    }
    
    /**
     * この処理はLeveMapでは何もしません.
     * @param set 例外が発生します.
     */
    public final void getAllValues( Set<Object> set ) {
        src.getAllValues( set ) ;
    }
    
    /**
     * この処理はLeveMapでは何もしません.
     * @return String 空文字が返却されます.
     */
    public final String toString() {
       return  src.toString() ;
    }
}
