package org.maachang.jsr.script.api;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;

/**
 * 基底API管理用マネージャ.
 * 
 * @version 2008/08/11
 * @author masahito suzuki
 * @since MaachangComet 1.23
 */
public class ApiManager {
    
    /**
     * スクリプトエンジン名.
     */
    public static final String ENGINE_NAME = "jss" ;
    
    /**
     * コアAPI用バインディングマップ.
     */
    private Map<String,Object> coreApiMap = null ;
    
    /**
     * Model用バインディングマップ.
     */
    private Map<String,Object> modelMap = null ;
    
    /**
     * ライブラリ用バインディングマップ.
     */
    private Map<String,Object> libMap = null ;
    
    /**
     * スクリプトマネージャ.
     */
    private ScriptEngineManager scriptEngineManager = new ScriptEngineManager() ;
    
    /**
     * シングルトン.
     */
    private static final ApiManager SNGL = new ApiManager() ;
    
    /** スレッドローカル **/
    private static final ThreadLocal<Map<String,Object>> LOCAL_SCOPE =
        new ThreadLocal<Map<String,Object>>() ;
    
    /**
     * 同期オブジェクト.
     */
    private final Object sync = new Object() ;
    
    /**
     * オブジェクトを取得します.
     * @return ApiManager オブジェクトが返されます.
     */
    public static final ApiManager getInstance() {
        return SNGL ;
    }
    
    /** コンストラクタ. */
    private ApiManager() {
    }
    
    /**
     * coreApiMap を取得.
     * <BR><BR>
     * @return coreApiMap が返されます.
     */
    public Map<String, Object> getCoreApiMap() {
        Map<String, Object> ret = null ;
        synchronized( sync ) {
            ret = coreApiMap ;
        }
        return ret;
    }
    
    /**
     * coreApiMap を設定.
     * <BR><BR>
     * @param coreApiMap coreApiMap を設定します.
     */
    public void setCoreApiMap(Map<String, Object> coreApiMap) {
        synchronized( sync ) {
            this.coreApiMap = coreApiMap;
        }
    }
    
    /**
     * libMap を取得.
     * <BR><BR>
     * @return libMap が返されます.
     */
    public Map<String, Object> getLibMap() {
        Map<String, Object> ret = null ;
        synchronized( sync ) {
            ret = libMap ;
        }
        return ret;
    }
    
    /**
     * libMap を設定.
     * <BR><BR>
     * @param libMap libMap を設定します.
     */
    public void setLibMap(Map<String, Object> libMap) {
        synchronized( sync ) {
            this.libMap = libMap;
        }
    }
    
    /**
     * modelMap を取得.
     * <BR><BR>
     * @return modelMap が返されます.
     */
    public Map<String, Object> getModelMap() {
        Map<String, Object> ret = null ;
        synchronized( sync ) {
            ret = modelMap ;
        }
        return ret;
    }
    
    /**
     * modelMap を設定.
     * <BR><BR>
     * @param modelMap modelMap を設定します.
     */
    public void setModelMap(Map<String, Object> modelMap) {
        synchronized( sync ) {
            this.modelMap = modelMap;
        }
    }
    
    /**
     * スクリプトエンジンマネージャを取得.
     * @return ScriptEngineManager スクリプトエンジンマネージャが返されます.
     */
    public ScriptEngineManager getScriptEngineManager() {
        return scriptEngineManager ;
    }
    
    /**
     * スクリプトエンジンを取得.
     * @return ScriptEngine スクリプトエンジンが返されます.
     * @exception Exception 例外.
     */
    public ScriptEngine getScriptEngine() throws Exception {
        return scriptEngineManager.getEngineByName( ENGINE_NAME ) ;
    }
    
    /**
     * APIマネージャ内に指定Key内容が存在するかチェック.
     * @param key 対象のキーを設定します.
     * @return boolean [true]の場合、内容が存在します.
     */
    public boolean containsKey(Object key) {
        if( key == null || ( key instanceof String ) == false ||
            ( ( String )key ).length() <= 0 ) {
            throw new IllegalArgumentException( "引数は不正です" ) ;
        }
        Map<String, Object> lib = null ;
        Map<String, Object> model = null ;
        Map<String, Object> coreApi = null ;
        synchronized( sync ) {
            lib = this.libMap ;
            model = this.modelMap ;
            coreApi = this.coreApiMap ;
        }
        
        if( lib != null && lib.containsKey(key) ) {
            return true ;
        }
        else if( model != null && model.containsKey(key) ) {
            return true ;
        }
        else if( coreApi != null && coreApi.containsKey(key) ) {
            return true ;
        }
        return false ;
    }
    
    /**
     * APIマネージャ内の指定Key内容を取得.
     * @param key 対象のキーを設定します.
     * @return Object 内容が返されます.
     */
    public Object get(Object key) {
        if( key == null || ( key instanceof String ) == false ||
            ( ( String )key ).length() <= 0 ) {
            throw new IllegalArgumentException( "引数は不正です" ) ;
        }
        Map<String, Object> lib = null ;
        Map<String, Object> model = null ;
        Map<String, Object> coreApi = null ;
        synchronized( sync ) {
            lib = this.libMap ;
            model = this.modelMap ;
            coreApi = this.coreApiMap ;
        }
        
        if( lib != null && lib.containsKey(key) ) {
            return lib.get( key ) ;
        }
        else if( model != null && model.containsKey(key) ) {
            return model.get( key ) ;
        }
        else if( coreApi != null && coreApi.containsKey(key) ) {
            return coreApi.get( key ) ;
        }
        return null ;
    }
    
    /**
     * APIマネージャ内のキー一覧を取得.
     * @param list Key一覧をセットするためのListを設定します.
     */
    public void getAllKey( ArrayList<String> list ) {
        Map<String, Object> lib = null ;
        Map<String, Object> model = null ;
        Map<String, Object> coreApi = null ;
        synchronized( sync ) {
            lib = this.libMap ;
            model = this.modelMap ;
            coreApi = this.coreApiMap ;
        }
        
        if( coreApi != null && coreApi.size() > 0 ) {
            Iterator ita = coreApi.keySet().iterator() ;
            while( ita.hasNext() ) {
                list.add( ( String )ita.next() ) ;
            }
        }
        if( model != null && model.size() > 0 ) {
            Iterator ita = model.keySet().iterator() ;
            while( ita.hasNext() ) {
                list.add( ( String )ita.next() ) ;
            }
        }
        if( lib != null && lib.size() > 0 ) {
            Iterator ita = lib.keySet().iterator() ;
            while( ita.hasNext() ) {
                list.add( ( String )ita.next() ) ;
            }
        }
    }
    
    /**
     * APIマネージャ内のキー一覧を取得.
     * @param list Key一覧をセットするためのListを設定します.
     */
    public void getAllKey( Set<String> set ) {
        Map<String, Object> lib = null ;
        Map<String, Object> model = null ;
        Map<String, Object> coreApi = null ;
        synchronized( sync ) {
            lib = this.libMap ;
            model = this.modelMap ;
            coreApi = this.coreApiMap ;
        }
        
        if( coreApi != null && coreApi.size() > 0 ) {
            Iterator ita = coreApi.keySet().iterator() ;
            while( ita.hasNext() ) {
                set.add( ( String )ita.next() ) ;
            }
        }
        if( model != null && model.size() > 0 ) {
            Iterator ita = model.keySet().iterator() ;
            while( ita.hasNext() ) {
                set.add( ( String )ita.next() ) ;
            }
        }
        if( lib != null && lib.size() > 0 ) {
            Iterator ita = lib.keySet().iterator() ;
            while( ita.hasNext() ) {
                set.add( ( String )ita.next() ) ;
            }
        }
    }
    
    /**
     * APIマネージャ内のキー数を取得.
     * @return int キー数が返されます.
     */
    public int size() {
        Map<String, Object> lib = null ;
        Map<String, Object> model = null ;
        Map<String, Object> coreApi = null ;
        synchronized( sync ) {
            lib = this.libMap ;
            model = this.modelMap ;
            coreApi = this.coreApiMap ;
        }
        
        int ret = 0 ;
        if( coreApi != null ) {
            ret += coreApi.size() ;
        }
        if( model != null ) {
            ret += model.size() ;
        }
        if( lib != null ) {
            ret += lib.size() ;
        }
        return ret ;
    }
    
    /**
     * スクリプト名を設定.
     * @param name スクリプト名を設定します.
     */
    public static final void setScriptName( String name ) {
        if( name == null || ( name = name.trim() ).length() <= 0 ) {
            return ;
        }
        getLocal().put( ScriptEngine.FILENAME,name ) ;
    }
    
    /**
     * スクリプト名を取得.
     * @return String スクリプト名が返されます.
     */
    public static final String getScriptName() {
        Object ret = getLocal().get( ScriptEngine.FILENAME ) ;
        if( ret == null || ( ret instanceof String ) == false ) {
            return null ;
        }
        return ( String )ret ;
    }
    
    /**
     * スレッドローカルに対して、Map情報を削除.
     */
    public static final void removeLocal() {
        LOCAL_SCOPE.remove() ;
        //Map<String,Object> ret = LOCAL_SCOPE.get() ;
        //if( ret != null ) {
        //    ret.clear() ;
        //}
    }
    
    /**
     * スレッドローカルから、Map情報を取得.
     * @return Map<String,Object> 対象のMapが返されます.
     */
    public static final Map<String,Object> getLocal() {
        Map<String,Object> ret = LOCAL_SCOPE.get() ;
        if( ret == null ) {
            ret = new HashMap<String,Object>() ;
            LOCAL_SCOPE.set( ret ) ;
        }
        return ret ;
    }
    
    /**
     * スレットローカルに対して、Map情報を設定.
     * @param map 対象のMap情報を設定します.
     */
    public static final void setLocal( Map<String,Object> map ) {
        LOCAL_SCOPE.set( map ) ;
    }
}
