package org.maachang.comet.httpd.engine.script.cache;

import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import javax.script.Bindings;
import javax.script.SimpleBindings;

/**
 * キャッシュテーブルWrapper.
 * 
 * @version 2007/09/23
 * @author masahito suzuki
 * @since MaachangComet 1.00
 */
public class CacheTableWrapper implements Map<String,Object> {
    
    /**
     * 現在ThreadのBindings.
     */
    private Map<String,Bindings> manager = null ;
    
    /**
     * CacheTable.
     */
    private CacheTable cacheTable = null ;
    
    /**
     * CacheTable追加モード.
     */
    private volatile boolean cacheAppend = false ;
    
    /**
     * CacheTable追加同期.
     */
    private final Object sync = new Object() ;
    
    /**
     * コンストラクタ.
     */
    public CacheTableWrapper() {
        this.manager = Collections.synchronizedMap( new HashMap<String,Bindings>() ) ;
        this.cacheTable = new CacheTable() ;
    }
    
    /**
     * コンストラクタ.
     * <BR><BR>
     * キャッシュテーブルを設定してオブジェクトを生成します.
     * <BR>
     * @param cacheTable 対象のキャッシュテーブルを設定します.
     * @exception Exception 例外.
     */
    public CacheTableWrapper( CacheTable cacheTable )
        throws Exception {
        if( cacheTable == null ) {
            throw new IllegalArgumentException( "引数は不正です" ) ;
        }
        this.cacheTable = cacheTable ;
    }
    
    /**
     * キャッシュテーブルを取得.
     * <BR><BR>
     * 格納されているキャッシュテーブルを取得します.
     * <BR>
     * @return CacheTable 対象のキャッシュテーブルが返されます.
     */
    public CacheTable getCacheTable() {
        this.cacheTable.waitUpdate() ;
        return this.cacheTable ;
    }
    
    /**
     * 対象のBindingsをクリア.
     * <BR><BR>
     * 対象のBindingsをクリアします.
     */
    public void clearThread() {
        this.cacheTable.waitUpdate() ;
        manager.remove( Thread.currentThread().getName() ) ;
    }
    
    /**
     * Cache追加モードを設定.
     * <BR><BR>
     * @param mode Cache追加モードを設定します.
     */
    public void setCacheAppend( boolean mode ) {
        synchronized( sync ) {
            cacheAppend = mode ;
        }
    }
    
    /**
     * Cache追加モードを取得.
     * <BR><BR>
     * Cache追加モードを取得します.
     * <BR>
     * @return boolean Cache追加モードが返されます.
     */
    public boolean isCacheAppend() {
        boolean ret = false ;
        synchronized( sync ) {
            ret = cacheAppend ;
        }
        return ret ;
    }
    
    /**
     * 全てのキャッシュ名一覧を取得.
     * <BR><BR>
     * 全てのキャッシュ名一覧を取得します.
     * <BR>
     * @return Object[] キャッシュ名一覧が返されます.
     */
    public Object[] getAllKey() {
        HashSet<String> keys = cacheTable.getAllKey() ;
        Bindings bin = getTable() ;
        if( bin != null && bin.size() > 0 ) {
            Object[] o = bin.keySet().toArray() ;
            int len = o.length ;
            for( int i = 0 ; i < len ; i ++ ) {
                keys.add( ( String )o[ i ] ) ;
            }
        }
        return keys.toArray() ;
    }
    
    public int size() {
        this.cacheTable.waitUpdate() ;
        if( isCacheAppend() == true ) {
            return cacheTable.size() ;
        }
        else {
            return getTable().size() + cacheTable.size() ;
        }
    }
    public void clear() {
        this.cacheTable.waitUpdate() ;
        getTable().clear() ;
    }
    public boolean isEmpty() {
        this.cacheTable.waitUpdate() ;
        if( isCacheAppend() == true ) {
            return cacheTable.isEmpty() ;
        }
        else {
            if( getTable().isEmpty() == false ) {
                return cacheTable.isEmpty() ;
            }
            return true ;
        }
    }
    public boolean containsKey(Object key) {
        this.cacheTable.waitUpdate() ;
        if( isCacheAppend() == true ) {
            return cacheTable.containsKey( key ) ;
        }
        else {
            if( getTable().containsKey( key ) == true ) {
                return true ;
            }
            if( cacheTable.containsKey( key ) == true ) {
                return true ;
            }
            return false ;
        }
    }
    public boolean containsValue(Object value) {
        this.cacheTable.waitUpdate() ;
        if( isCacheAppend() == true ) {
            return cacheTable.containsValue( value ) ;
        }
        else {
            if( getTable().containsValue( value ) == true ) {
                return true ;
            }
            if( cacheTable.containsValue( value ) == true ) {
                return true ;
            }
            return false ;
        }
    }
    public Object get(Object key) {
        this.cacheTable.waitUpdate() ;
        if( isCacheAppend() == true ) {
            return cacheTable.get( key ) ;
        }
        else {
            Object o = getTable().get( key ) ;
            if( o != null ) {
                return o ;
            }
            return cacheTable.get( key ) ;
        }
    }
    public Object put(String key, Object value) {
        this.cacheTable.waitUpdate() ;
        if( isCacheAppend() == true ) {
            return cacheTable.put( key,value ) ;
        }
        else {
            return getTable().put( key,value ) ;
        }
    }
    public Object remove(Object key) {
        this.cacheTable.waitUpdate() ;
        return getTable().remove( key ) ;
    }
    public void putAll(Map<? extends String, ? extends Object> m) {
    }
    public Set<String> keySet() {
        return null ;
    }
    public Collection<Object> values() {
        return null ;
    }
    public Set<Map.Entry<String, Object>> entrySet() {
        return null ;
    }
    public boolean equals(Map<String,Object> o) {
        return false ;
    }
    public int hashCode() {
        return 1 ;
    }
    private synchronized Bindings getTable() {
        String name = Thread.currentThread().getName() ;
        Bindings ret = manager.get( name ) ;
        if( ret == null ) {
            ret = new SimpleBindings() ;
            manager.put( name,ret ) ;
        }
        return ret ;
    }
}
