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

import java.util.Iterator;
import java.util.Map;

import javax.script.Bindings;
import javax.script.ScriptContext;

import org.maachang.comet.ServiceDef;
import org.maachang.comet.httpd.engine.script.SrcScript;
import org.maachang.jsr.script.api.ApiManager;
import org.maachang.jsr.script.api.ExternalBindings;
import org.maachang.manager.GlobalManager;
import org.maachang.util.ObjectArray;

/**
 * キャッシュスクリプト管理.
 * 
 * @version 2007/11/12
 * @author masahito suzuki
 * @since MaachangComet 1.00
 */
public class CacheScriptManager {
    
    /**
     * コアAPI用マネージャ.
     */
    private CoreApiManager coreApiManager = null ;
    
    /**
     * ModelAPI用マネージャ.
     */
    private ModelApiManager modelApiManager = null ;
    
    /**
     * ライブラリAPI用マネージャ.
     */
    private LibApiManager libApiManager = null ;
    
    /**
     * 自オブジェクト.
     */
    private static CacheScriptManager thisObject = null ;
    
    /**
     * キャッシュスクリプト初期化処理.
     * @exception Exception 例外.
     */
    public static final void init()
        throws Exception {
        thisObject = new CacheScriptManager() ;
        GlobalManager.getInstance().put( ServiceDef.SCRIPT_CACHE_MANAGER,thisObject ) ;
    }
    
    /**
     * キャッシュスクリプトマネージャを取得.
     * <BR><BR>
     * キャッシュスクリプトマネージャを取得します.
     * <BR>
     * @return CacheScriptManager キャッシュスクリプトマネージャが返されます.
     */
    public static final CacheScriptManager getInstance() {
        return thisObject ;
    }
    
    /**
     * コンストラクタ.
     */
    private CacheScriptManager() throws Exception {
        this.coreApiManager = new CoreApiManager() ;
        this.modelApiManager = new ModelApiManager() ;
        this.libApiManager = new LibApiManager() ;
    }
    
    /**
     * デストラクタ.
     */
    protected void finalize() throws Exception {
        this.destroy() ;
    }
    
    /**
     * オブジェクト破棄.
     * <BR><BR>
     * オブジェクトを破棄します.
     */
    protected void destroy() {
        this.coreApiManager = null ;
        this.modelApiManager = null ;
        this.libApiManager = null ;
    }
    
    /**
     * リロード処理.
     * <BR><BR>
     * リロード処理を実施します.
     * <BR>
     * @exception Exception 例外.
     */
    public void reload() throws Exception {
        this.modelApiManager.reload() ;
        this.libApiManager.reload() ;
    }
    
    /**
     * 指定内容をリロード.
     * <BR><BR>
     * 指定された内容だけをリロードします.
     * <BR>
     * @param name リロード対象の条件を設定します.<BR>
     *             [null]の場合、全体をリロードします.
     * @exception Exception 例外.
     */
    public void reload( String name ) throws Exception {
        if( name == null || ( name = name.trim() ).length() <= 0 ) {
            this.reload() ;
        }
        else {
            name = name.toLowerCase() ;
            if( "model".equals( name ) ) {
                this.modelApiManager.reload() ;
            }
            else if( "lib".equals( name ) ) {
                this.libApiManager.reload() ;
            }
            else {
                this.reload() ;
            }
        }
    }
    
    /**
     * バインディングスオブジェクトを取得.
     * @return Bindings バインディングスオブジェクトが返されます.
     * @exception Exception 例外.
     */
    public Bindings getBindings() throws Exception {
        this.modelApiManager.useScript() ;
        this.libApiManager.useScript() ;
        return ExternalBindings.getInstance() ;
    }
    
    /**
     * スクリプトキャッシュを取得.
     * <BR><BR>
     * スクリプトキャッシュを取得します.
     * <BR>
     * @param ctx 取得対象のスクリプトコンテキストを設定します.
     * @exception Exception 例外.
     */
    public void script( ScriptContext ctx ) throws Exception {
        this.modelApiManager.useScript() ;
        this.libApiManager.useScript() ;
        ctx.setBindings( ExternalBindings.getInstance(),ScriptContext.ENGINE_SCOPE ) ;
    }
    
    /**
     * スクリプト終了処理.
     */
    public void exitScript() {
        ApiManager.removeLocal() ;
    }
    
    /**
     * 指定パスのソーススクリプトを取得.
     * <BR><BR>
     * 指定パスのソーススクリプトを取得します.
     * <BR>
     * @param path 対象のパス名を設定します.
     * @return SrcScript 対象のソーススクリプトが返されます.
     */
    public SrcScript getSrcScript( String path ) {
        if( path == null || ( path = path.trim() ).length() <= 0 ) {
            return null ;
        }
        //path = path.toLowerCase() ;
        if( this.coreApiManager.isCache( path ) ) {
            return coreApiManager.getSrcScript( path ) ;
        }
        if( this.modelApiManager.isCache( path ) ) {
            modelApiManager.setReloadFlag( true ) ;
            return modelApiManager.getSrcScript() ;
        }
        if( this.libApiManager.isCache( path ) ) {
            libApiManager.setReloadFlag( true ) ;
            return libApiManager.getSrcScript() ;
        }
        return null ;
    }
    
    /**
     * 指定パスの内容が存在するかチェック.
     * <BR><BR>
     * 指定パスの内容が存在するかチェックします.
     * <BR>
     * @param path 対象のパス名を設定します.
     * @return boolean [true]の場合、存在します.
     */
    public boolean isCache( String path ) {
        if( path == null || ( path = path.trim() ).length() <= 0 ) {
            return false ;
        }
        //path = path.toLowerCase() ;
        if( this.coreApiManager.isCache( path ) ) {
            return true ;
        }
        if( this.modelApiManager.isCache( path ) ) {
            return true ;
        }
        if( this.libApiManager.isCache( path ) ) {
            return true ;
        }
        return false ;
    }
    
    /**
     * 指定パスのディレクトリ名を取得.
     * <BR><BR>
     * 指定パスのディレクトリ名を取得します.
     * <BR>
     * @param path 対象のパス名を設定します.
     * @return String 対象のディレクトリが返されます.
     */
    public String getDirectory( String path ) {
        if( path == null || ( path = path.trim() ).length() <= 0 ) {
            return "" ;
        }
        if( this.coreApiManager.isCache( path ) ) {
            return "" ;
        }
        if( this.modelApiManager.isCache( path ) ) {
            return modelApiManager.getDirectory() ;
        }
        if( this.libApiManager.isCache( path ) ) {
            return libApiManager.getDirectory() ;
        }
        return "" ;
    }
    
    /**
     * 読み込み名一覧を取得.
     * <BR><BR>
     * 現在読み込んでいる名前一覧を取得します.
     * <BR>
     * @param bindings 対象のバインディングスを設定します.
     * @return String[] 読み込み名一覧が返されます.
     */
    public String[] getNames( Bindings bindings ) {
        ObjectArray lst = new ObjectArray() ;
        pushMapKeys( lst,bindings ) ;
        int len = lst.size() ;
        if( len > 0 ) {
            lst.sort() ;
            String[] ret = new String[ len ] ;
            System.arraycopy( lst.getObjects(),0,ret,0,len ) ;
            lst.clear() ;
            return ret ;
        }
        return null ;
    }
    
    private void pushMapKeys( ObjectArray lst,Map<String,Object> map ) {
        if( map != null ) {
            Iterator ita = map.keySet().iterator() ;
            while( ita.hasNext() ) {
                String key = ( String )ita.next() ;
                if( key != null && key.length() > 0 ) {
                    lst.add( key ) ;
                }
            }
        }
    }
}

