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

import org.maachang.comet.MaachangDef;
import org.maachang.comet.httpd.HttpdErrorDef;
import org.maachang.comet.httpd.HttpdStateException;
import org.maachang.comet.httpd.engine.script.ExecutionScript;
import org.maachang.comet.httpd.engine.script.Script;
import org.maachang.comet.httpd.engine.script.ScriptDef;
import org.maachang.comet.httpd.engine.script.SrcScript;
import org.maachang.comet.httpd.engine.script.js.JsDef;
import org.maachang.util.FileUtil;

/**
 * Modelスクリプト.
 * 
 * @version 2007/08/29
 * @author masahito suzuki
 * @since MaachangComet 1.00
 */
public class ModelScript implements Script {
    
    /**
     * モデル名.
     */
    private String name = null ;
    
    /**
     * モデルファイル名.
     */
    private String modelFileName = null ;
    
    /**
     * ファイル更新時間.
     */
    private long updateTime = -1L ;
    
    /**
     * スクリプト内容.
     */
    private SrcScript srcScript = null ;
    
    /**
     * 実行スクリプトオブジェクト.
     */
    private ExecutionScriptImpl executionScript = null ;
    
    /**
     * デフォルトパッケージ更新ID.
     */
    private int packageId = -1 ;
    
    /**
     * コンストラクタ.
     */
    private ModelScript() {
        
    }
    
    /**
     * コンストラクタ.
     * <BR><BR>
     * 条件を指定してオブジェクトを生成します.
     * <BR>
     * @param name 対象のモデル名を設定します.
     * @param currentDirectory カレントディレクトリを設定します.
     * @exception Exception 例外.
     */
    public ModelScript( String name,String currentDirectory )
        throws Exception {
        if( name == null || ( name = name.trim() ).length() <= 0 ) {
            throw new HttpdStateException( HttpdErrorDef.HTTP11_500,
                "モデル名は不正です" ) ;
        }
        if( name.endsWith( ScriptDef.SCRIPT_PLUS ) == false ) {
            name += ScriptDef.SCRIPT_PLUS ;
        }
        currentDirectory = ScriptDef.trimCurrentDirectory( currentDirectory ) ;
        String modelFileName = new StringBuilder().append( currentDirectory ).
            append( MaachangDef.DIRECTORY_MODEL ).append( name ).toString() ;
        if( FileUtil.isFileExists( modelFileName ) == false ) {
            throw new HttpdStateException( HttpdErrorDef.HTTP11_500,
                "Modelファイル[" + name + "]は存在しません" ) ;
        }
        this.name = name ;
        this.modelFileName = modelFileName ;
    }
    
    /**
     * 情報クリア.
     */
    protected void clear() {
        updateTime = -1L ;
        srcScript = null ;
        executionScript = null ;
    }
    
    /**
     * 基本スクリプトを取得.
     * <BR><BR>
     * 基本スクリプトを取得します.
     * <BR>
     * @return ExecutionScript 基本スクリプトが返されます.
     * @exception Exception 例外.
     */
    public synchronized ExecutionScript getScript() throws Exception {
        if( FileUtil.isFileExists( modelFileName ) == false ) {
            clear() ;
            throw new HttpdStateException( HttpdErrorDef.HTTP11_500,
                "Modelファイル[" + name + "]は存在しません" ) ;
        }
        else if( executionScript == null ||
            this.updateTime != FileUtil.getLastTime( modelFileName ) ||
            this.packageId != JsDef.getDefaultPackageId() ) {
            readScript() ;
        }
        return executionScript ;
    }
    
    /**
     * スクリプトパスを取得.
     * <BR><BR>
     * スクリプトパスを取得します.
     * <BR>
     * @return String スクリプトパスが返されます.
     */
    public synchronized String getPath() {
        return this.name ;
    }
    
    /**
     * スクリプトファイル名を取得.
     * <BR><BR>
     * スクリプトファイル名を取得します.
     * <BR>
     * @return String スクリプトファイル名が返されます.
     */
    public synchronized String getScriptName() {
        return this.modelFileName ;
    }
    
    /**
     * スクリプトタイプを取得.
     * <BR><BR>
     * スクリプトタイプを取得します.
     * <BR>
     * @return int スクリプトタイプが返されます.
     */
    public int getScriptType() {
        return ScriptDef.SCRIPT_TYPE_BY_MODEL ;
    }
    
    /**
     * スクリプト内容を取得.
     * <BR><BR>
     * スクリプト内容を取得します.
     * <BR>
     * @return SrcScript スクリプト内容が返されます.
     */
    public synchronized SrcScript getSrcScript() {
        return srcScript ;
    }
    
    /**
     * スクリプトが利用可能かチェック.
     * <BR><BR>
     * スクリプトが利用可能かチェックします.
     * <BR>
     * @return boolean [true]の場合、利用可能です.
     */
    public synchronized boolean isScript() {
        return ( executionScript == null ) ? false : true ;
    }
    
    /**
     * スクリプト内容を読み込む.
     */
    private void readScript()
        throws Exception {
        StringBuilder buf = new StringBuilder() ;
        this.packageId = JsDef.pushDefaultPackage( buf ) ;
        buf.append( FileUtil.getFileByString( modelFileName,CHARSET ) ) ;
        String script = buf.toString() ;
        buf = null ;
        script = ReadScriptPlus.convert( true,script ) ;
        this.srcScript = new SrcScript( script ) ;
        this.executionScript = new ExecutionScriptImpl(
            ScriptDef.ENGINE_JAVASCRIPT,ScriptDef.SCRIPT_TYPE_BY_MODEL,
            this.getPath(),script ) ;
        buf = null ;
        this.updateTime = FileUtil.getLastTime( modelFileName ) ;
    }
}
