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.comet.httpd.engine.script.taglib.AnalysisTagLib;
import org.maachang.util.FileUtil;

/**
 * MHTMLスクリプト情報.
 * 
 * @version 2007/08/29
 * @author masahito suzuki
 * @since MaachangComet 1.00
 */
public class MhtmlScript implements Script {
    
    /**
     * 対象パス.
     */
    protected String path = null ;
    
    /**
     * ファイル名.
     */
    protected String fileName = null ;
    
    /**
     * ファイル更新時間.
     */
    protected long updateTime = -1L ;
    
    /**
     * スクリプト内容.
     */
    private SrcScript srcScript = null ;
    
    /**
     * 実行スクリプトオブジェクト.
     */
    protected ExecutionScriptImpl executionScript = null ;
    
    /**
     * デフォルトパッケージ更新ID.
     */
    private int packageId = -1 ;
    
    /**
     * ページキャッシュ時間.
     */
    private long cacheTime = -1L ;
    
    
    /**
     * コンストラクタ.
     */
    private MhtmlScript() {
    }
    
    /**
     * コンストラクタ.
     * <BR><BR>
     * 条件を指定してオブジェクトを生成します.
     * <BR>
     * @param path 対象のパスを設定します.
     * @param currentDirectory カレントディレクトリを設定します.
     * @exception Exception 例外.
     */
    public MhtmlScript( String path,String currentDirectory )
        throws Exception {
        if( path == null || ( path = path.trim() ).length() <= 0 ) {
            throw new HttpdStateException( HttpdErrorDef.HTTP11_500,
                "mhtmlの指定パスは不正です" ) ;
        }
        if( path.endsWith( ScriptManager.SCRIPT_HTML_PLUS ) == false ) {
            path += ScriptManager.SCRIPT_HTML_PLUS ;
        }
        currentDirectory = ScriptDef.trimCurrentDirectory( currentDirectory ) ;
        String fileName = new StringBuilder().append( currentDirectory ).
            append( MaachangDef.DIRECTORY_APPLICATION ).
            append( ( ( path.startsWith("/" ) ) ? path.substring( 1,path.length() ) : path ) ).
            toString() ;
        if( FileUtil.isFileExists( fileName ) == false ) {
            throw new HttpdStateException( HttpdErrorDef.HTTP11_500,
                "対象mhtmlファイル[" + path + "]は存在しません" ) ;
        }
        this.path = path ;
        this.fileName = fileName ;
    }
    
    /**
     * 情報クリア.
     */
    protected void clear() {
        updateTime = -1L ;
        srcScript = null ;
        executionScript = null ;
        cacheTime = -1L ;
    }
    
    /**
     * 基本スクリプトを取得.
     * <BR><BR>
     * 基本スクリプトを取得します.
     * <BR>
     * @return ExecutionScript 基本スクリプトが返されます.
     * @exception Exception 例外.
     */
    public synchronized ExecutionScript getScript() throws Exception {
        if( FileUtil.isFileExists( fileName ) == false ) {
            this.clear() ;
            throw new HttpdStateException( HttpdErrorDef.HTTP11_500,
                "対象mhtmlファイル[" + path + "]は存在しません" ) ;
        }
        else if( executionScript == null ||
            this.updateTime != FileUtil.getLastTime( fileName ) ||
            this.packageId != JsDef.getDefaultPackageId() ) {
            readScript() ;
        }
        return executionScript ;
    }
    
    /**
     * スクリプトパスを取得.
     * <BR><BR>
     * スクリプトパスを取得します.
     * <BR>
     * @return String スクリプトパスが返されます.
     */
    public synchronized String getPath() {
        return this.path ;
    }
    
    /**
     * スクリプトファイル名を取得.
     * <BR><BR>
     * スクリプトファイル名を取得します.
     * <BR>
     * @return String スクリプトファイル名が返されます.
     */
    public synchronized String getScriptName() {
        return this.fileName ;
    }
    
    /**
     * スクリプトタイプを取得.
     * <BR><BR>
     * スクリプトタイプを取得します.
     * <BR>
     * @return int スクリプトタイプが返されます.
     */
    public int getScriptType() {
        return ScriptManager.SCRIPT_TYPE_BY_HTML ;
    }
    
    /**
     * スクリプト内容を取得.
     * <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 ;
    }
    
    /**
     * ページキャッシュモードであるかチェック.
     * <BR><BR>
     * ページキャッシュモードであるかチェックします.
     * <BR>
     * @return boolean [true]の場合、ページキャッシュモードです.
     */
    public synchronized boolean isPageCache() {
        return ( cacheTime > 0L ) ? true : false ;
    }
    
    /**
     * ページキャッシュ時間を取得.
     * <BR><BR>
     * ページキャッシュ時間が返されます.
     * <BR>
     * @return long ページキャッシュ時間が返されます.<BR>
     *              [-1L]の場合は、ページキャッシュされません.
     */
    public synchronized long getPageCacheTime() {
        return cacheTime ;
    }
    
    /**
     * スクリプト内容を読み込む.
     */
    protected void readScript()
        throws Exception {
        String script = FileUtil.getFileByString( fileName,CHARSET ) ;
        if( script == null ) {
            clear() ;
            return ;
        }
        // ページキャッシュするか確認する.
        String[] sc = new String[]{script} ;
        long tm = PageCache.getInstance().analysisCacheMode( sc ) ;
        if( tm > 0L ) {
            script = sc[ 0 ] ;
            sc = null ;
            script = ReadScriptPlus.directInclude( "./",script ) ;
            script = AnalysisTagLib.analysis( script ) ;
            script = TemplateScriptAnalysis.analysis( script ) ;
            String mhtmlName = path.substring( 0,path.length() - ScriptManager.SCRIPT_HTML_PLUS.length() ) ;
            // スクリプトを書き換えて、キャッシュ格納形式に変換.
            StringBuilder buf = new StringBuilder() ;
            // ヘッダ付加.
            buf.
            append( "var __$page_cache_flag = true ;\n" ).
            append( "if( isNull( _$global." ).append( ScriptDef.SCRIPT_LAST_CONTROLLER ).append( " ) == false && " ).
            append( " _$global." ).append( ScriptDef.SCRIPT_LAST_CONTROLLER ).append( " != \"" ).append( mhtmlName ).append( "\"" ).
            append( " ) { __$page_cache_flag = false ; }\n" ) ;
            buf.
            append( "if( __$page_cache_flag ) { \n" ).
            append( "var " ).append( PageCache.OUTPUT_METHOD ).append( " = new StrBuf() ;\r\n" ).
            append( "httpPrint = function( str ) { if( useString( str ) ) { " ).append( PageCache.OUTPUT_METHOD ).append( ".ad( str ); } } ;\n" ).
            append( " } \n" ) ;
            // 対象スクリプト付加.
            buf.
            append( script ).append( "\n" ) ;
            // フッダ付加.
            buf.
            append( "if( __$page_cache_flag ) { \n" ).
            append( "var _$_$_out_$_$_ = " ).append( PageCache.OUTPUT_METHOD ).append( ".ts() ;\n" ).
            append( PageCache.OUTPUT_METHOD ).append( ".clear() ;\n" ).append( PageCache.OUTPUT_METHOD ).append( " = null ;\n" ).
            append( "org.maachang.comet.httpd.engine.script.scripts.PageCache.getInstance().set( \"" ).append( path ).append( "\"," ).
                append( tm ).append( ",_$_$_out_$_$_ ) ;\n" ).
            append( "httpPrintln( _$_$_out_$_$_ ) ;\n" ).
            append( " } \n" ) ;
            script = buf.toString() ;
            buf = null ;
            cacheTime = tm ;
        }
        // ページキャッシュしない場合の処理.
        else {
            sc = null ;
            script = ReadScriptPlus.directInclude( "./",script ) ;
            script = AnalysisTagLib.analysis( script ) ;
            script = TemplateScriptAnalysis.analysis( script ) ;
            // 出力内容は、まとめて返信するように変更.
            StringBuilder buf = new StringBuilder() ;
            buf.append( "var ___$_$_$rs$buf$_$ = new StrBuf() ;\n" ).
                append( "httpPrint = function( str ) { if( useString( str ) ) { " ).append( "___$_$_$rs$buf$_$.ad( str ); } } ;\n" ).
                append( script ).append( "\n" ).
                append( "httpPrintln( ___$_$_$rs$buf$_$.ts() ) ;\n ___$_$_$rs$buf$_$.clear();\n___$_$_$rs$buf$_$=null;\n" ) ;
            script = buf.toString() ;
            buf = null ;
            cacheTime = -1L ;
        }
        StringBuilder buf = new StringBuilder() ;
        this.packageId = JsDef.pushDefaultPackage( buf ) ;
        ReadScriptPlus.addMHTML( buf,script,path ) ;
        script = buf.toString() ;
        buf = null ;
        
        script = ReadScriptPlus.convert( false,script ) ;
        this.srcScript = new SrcScript( script ) ;
        this.executionScript = new ExecutionScriptImpl(
            ScriptDef.ENGINE_JAVASCRIPT,getScriptType(),
            this.getPath(),script ) ;
        script = null ;
        this.updateTime = FileUtil.getLastTime( fileName ) ;
    }
    
}
