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

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import javax.script.Bindings;
import javax.script.Compilable;
import javax.script.CompiledScript;
import javax.script.ScriptEngine;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.maachang.comet.ServiceDef;
import org.maachang.comet.httpd.engine.script.ScriptDef;
import org.maachang.comet.httpd.engine.script.SrcScript;
import org.maachang.comet.httpd.engine.script.dao.MasterCache;
import org.maachang.jsr.script.api.ApiManager;
import org.maachang.jsr.script.api.ExternalBindings;
import org.maachang.manager.GlobalManager;

/**
 * コアAPIマネージャ.
 * 
 * @version 2008/08/11
 * @author masahito suzuki
 * @since MaachangComet 1.23
 */
class CoreApiManager {
    
    /**
     * LOG.
     */
    private static final Log LOG = LogFactory.getLog( CoreApiManager.class ) ;
    
    /**
     * スクリプトエンジン名.
     */
    protected static final String ENGINE_NAME = "js" ;
    
    /**
     * 読み込みデフォルトスクリプト一覧.
     */
    private static final String[] LIST = {
        "org/maachang/comet/httpd/engine/script/js/default.js",
        "org/maachang/comet/httpd/engine/script/js/log.js",
        "org/maachang/comet/httpd/engine/script/js/exception.js",
        "org/maachang/comet/httpd/engine/script/js/io.js",
        "org/maachang/comet/httpd/engine/script/js/file_list.js",
        "org/maachang/comet/httpd/engine/script/js/html.js",
        "org/maachang/comet/httpd/engine/script/js/validate.js",
        "org/maachang/comet/httpd/engine/script/js/tag.js",
        "org/maachang/comet/httpd/engine/script/js/master_cache.js",
        "org/maachang/comet/httpd/engine/script/js/record.js",
        "org/maachang/comet/httpd/engine/script/js/base_model.js",
        "org/maachang/comet/httpd/engine/script/js/image.js",
        "org/maachang/comet/httpd/engine/script/js/mdbm.js",
        "org/maachang/comet/httpd/engine/script/js/http_client.js",
        "org/maachang/comet/httpd/engine/script/js/mail.js",
        "org/maachang/comet/httpd/engine/script/js/template.js",
        "org/maachang/comet/httpd/engine/script/js/report.js",
        "org/maachang/comet/httpd/engine/script/js/rpc.js",
        "org/maachang/comet/httpd/engine/script/js/comet.js",
        "org/maachang/comet/httpd/engine/script/js/utils.js",
        "org/maachang/comet/httpd/engine/script/js/mttml.js",
        "org/maachang/comet/httpd/engine/script/js/startup.js"
    } ;
    
    /**
     * 読み込みデフォルトスクリプト名.
     */
    private static final String[] LIST_NAME = {
        "*base_script",
        "*log",
        "*exception",
        "*io",
        "*file_list",
        "*html",
        "*validate",
        "*tag",
        "*master_cache",
        "*record",
        "*base_model",
        "*image",
        "*mdbm",
        "*http_client",
        "*mail",
        "*template",
        "*report",
        "*rpc",
        "*comet",
        "*utils",
        "*mttml",
        "*startup"
    } ;
    
    /**
     * スクリプト名接頭語.
     */
    private static final String HEAD = "*" ;
    
    /**
     * ソーススクリプト.
     */
    private HashMap<String,SrcScript> srcScripts = null ;
    
    /**
     * 同期オブジェクト.
     */
    private final Object sync = new Object() ;
    
    /**
     * コンストラクタ.
     */
    public CoreApiManager() throws Exception {
        try {
            loadScripts() ;
        } catch( Exception e ) {
            LOG.error( "## [coreApi] loadScriptError",e ) ;
            throw e ;
        }
    }
    
    /**
     * 指定パスのソーススクリプトを取得.
     * <BR><BR>
     * 指定パスのソーススクリプトを取得します.
     * <BR>
     * @param path 対象のパス名を設定します.
     * @return SrcScript 対象のソーススクリプトが返されます.
     */
    public SrcScript getSrcScript( String path ) {
        if( isCache( path ) == false ) {
            return null ;
        }
        synchronized( sync ) {
            if( srcScripts != null ) {
                return srcScripts.get( path ) ;
            }
        }
        return null ;
    }
    
    /**
     * 指定パスがこのキャッシュ内の条件であるかチェック.
     * <BR><BR>
     * 指定パスがこのキャッシュ内の条件であるかチェックします.
     * <BR>
     * @param path チェック対象のパスを設定します.
     * @return boolean [true]の場合はこのキャッシュ内の条件です.
     */
    public boolean isCache( String path ) {
        if( path == null || ( path = path.trim() ).length() <= 0 ||
            path.startsWith( HEAD ) == false ) {
            return false ;
        }
        return true ;
    }
    
    /**
     * スクリプトをロードして実行.
     */
    private void loadScripts() throws Exception {
        //ScriptContext ctx = null ;
        int len = LIST.length ;
        try {
            // コアAPIを実行して登録する.
            MasterCache masterCache = new MasterCache() ;
            Map<String,Object> shared = Collections.synchronizedMap( new HashMap<String,Object>() ) ;
            GlobalManager.getInstance().put( ServiceDef.MASTER_DB_CACHE_MANAGER,masterCache ) ;
            GlobalManager.getInstance().put( ServiceDef.SHARED_MEMORY_MANAGER,shared ) ;
            Bindings bindings = ExternalBindings.getInstance() ;
            bindings.put( ScriptDef.SCRIPT_BY_CTX,"" ) ;
            bindings.put( ScriptDef.MANAGER,GlobalManager.getInstance() ) ;
            bindings.put( ScriptDef.SCRIPT_CACHE_DB,masterCache ) ;
            bindings.put( ScriptDef.SCRIPT_SHARED,shared ) ;
            
            // ScrpitEngine実行.
            ScriptEngine engine = ApiManager.getInstance().getScriptEngine() ;
            //ctx = new SimpleScriptContext() ;
            //ctx.setBindings( bindings,ScriptContext.ENGINE_SCOPE ) ;
            //engine.setContext( ctx ) ;
            this.srcScripts = new HashMap<String,SrcScript>() ;
            for( int i = 0 ; i < len ; i ++ ) {
                if( LOG.isDebugEnabled() ) {
                    LOG.debug( ">read[coreApi] - " + LIST_NAME[ i ] ) ;
                }
                String script = ScriptDef.getScriptByResource( LIST[ i ] ) ;
                this.srcScripts.put( LIST_NAME[ i ],new SrcScript( script ) ) ;
                //engine.put( ScriptEngine.FILENAME,LIST_NAME[ i ] ) ;
                ApiManager.setScriptName( LIST_NAME[ i ] ) ;
                CompiledScript cs = ( ( Compilable )engine ).compile( script ) ;
                //cs.eval( ctx ) ;
                cs.eval( bindings ) ;
                cs = null ;
            }
            
            // APIマネージャに実行結果を登録.
            Map<String,Object> coreApi = ApiManager.getLocal() ;
            bindings.clear() ;
            ApiManager.getInstance().setCoreApiMap( coreApi ) ;
            if( LOG.isDebugEnabled() ) {
                LOG.debug( "** coreApi-cacheを読み込み" ) ;
            }
        } catch( Exception e ) {
            throw e ;
        } finally {
            ApiManager.removeLocal() ;
        }
    }
}

