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

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.util.HashSet;

import org.maachang.conf.Config;
import org.maachang.conf.ReadIni;
import org.maachang.util.Digest;
import org.maachang.util.DirUtil;
import org.maachang.util.FileUtil;

/**
 * タグライブラリ定義管理.
 * 
 * @version 2008/07/19
 * @author masahito suzuki
 * @since MaachangComet 1.20
 */
public class DefineTagLib {
    
    /**
     * デフォルト読み込みタグライブラリ.
     */
    private static final String[] DEF_TAGLIB = {
        "org/maachang/comet/httpd/engine/script/js/tag.tlib"
    } ;
    
    /**
     * 拡張タグライブラリ読み込みディレクトリ.
     */
    private static final String CUSTOM_TAGLIB_DIR = "./taglib" ;
    
    /**
     * 拡張タグ拡張子.
     */
    private static final String CUSTOM_TAGLIB_PLUS = ".tlib" ;
    
    /**
     * 再読み込み確認時間.
     */
    private static final long CHECK_TIME = 30000L ;
    
    /**
     * イベント条件群.
     */
    private static final String[] EVENTS = {
        "onclick",
        "ondblclick",
        "onkeydown",
        "onkeypress",
        "onkeyup",
        "onkeyup",
        "onmousedown",
        "onmouseup",
        "onmouseover",
        "onmouseout",
        "onmousemove",
        "onload",
        "onunload",
        "onfocus",
        "onblur",
        "onsubmit",
        "onreset",
        "onchange",
        "onresize",
        "onmove",
        "ondragdrop",
        "onabort",
        "onerror",
        "onselect"
    } ;
    
    /**
     * 更新確認コード.
     */
    private String sha1 = null ;
    
    /**
     * 前回確認時間.
     */
    private long beforeTiem = -1L ;
    
    /**
     * デフォルト読み込みタグライブラリ用コンフィグファイル.
     */
    private Config defConfig = null ;
    
    /**
     * データ管理.
     */
    private Config custom = null ;
    
    /**
     * イベントチェック用.
     */
    private HashSet<String> event = null ;
    
    /**
     * シングルトン.
     */
    private static final DefineTagLib SNGL = new DefineTagLib() ;
    
    /**
     * オブジェクトを取得.
     * @return DefineTagLib オブジェクトが返されます.
     */
    public static final DefineTagLib getInstance() {
        return SNGL ;
    }
    
    /**
     * コンストラクタ.
     */
    private DefineTagLib() {
    }
    
    /**
     * 初期化処理.
     */
    private void init() throws Exception {
        if( defConfig == null ) {
            this.sha1 = null ;
            this.beforeTiem = -1L ;
            this.defConfig = readDefault() ;
            this.custom = new Config() ;
            event = new HashSet<String>() ;
            for( int i = 0 ; i < EVENTS.length ; i ++ ) {
                event.add( EVENTS[ i ] ) ;
            }
        }
    }
    
    /**
     * 指定タグライブラリ名の内容を含むコンフィグ情報を取得.
     * @param name 対象のタグライブラリ名を設定します.
     * @return Config コンフィグ情報が返されます.
     * @exception Exception 例外.
     */
    public synchronized Config getConfig( String name ) throws Exception {
        this.init() ;
        if( defConfig.isSection( name ) ) {
            return defConfig ;
        }
        else {
            if( CHECK_TIME + beforeTiem <= System.currentTimeMillis() ) {
                beforeTiem = System.currentTimeMillis() ;
                String chk = getSha1() ;
                if( chk == null ) {
                    custom.clear() ;
                }
                else if( chk != sha1 ) {
                    readCustom( custom ) ;
                }
            }
            if( custom.isSection( name ) ) {
                return custom ;
            }
        }
        return null ;
    }
    
    /**
     * イベント要素チェック.
     * @param key 対象のキーを設定します.
     * @return boolean [true]の場合、イベント情報です.
     */
    public synchronized boolean isEvent( String key ) throws Exception {
        this.init() ;
        return event.contains( key ) ;
    }
    
    /**
     * デフォルトタグライブラリを読み込む.
     */
    private static final Config readDefault() throws Exception {
        BufferedReader br = null ;
        try {
            Config ret = new Config() ;
            int len = DEF_TAGLIB.length ;
            for( int i = 0 ; i < len ; i ++ ) {
                br = new BufferedReader( new InputStreamReader(
                        Thread.currentThread().getContextClassLoader().getResourceAsStream( DEF_TAGLIB[ i ] ),"UTF8" ) ) ;
                ReadIni.analisys( ret,br ) ;
                br.close() ;
                br = null ;
            }
            return ret ;
        } finally {
            if( br != null ) {
                try {
                    br.close() ;
                } catch( Exception e ) {
                }
            }
        }
    }
    
    /**
     * 拡張タグライブラリを読み込む.
     */
    private static final void readCustom( Config custom ) throws Exception {
        if( FileUtil.isDirExists( CUSTOM_TAGLIB_DIR ) == false ) {
            custom.clear() ;
            return ;
        }
        String[] list = DirUtil.getDirByUseFileName( CUSTOM_TAGLIB_DIR,CUSTOM_TAGLIB_PLUS ) ;
        if( list == null || list.length <= 0 ) {
            return ;
        }
        custom.clear() ;
        int len = list.length ;
        for( int i = 0 ; i < len ; i ++ ) {
            String file = CUSTOM_TAGLIB_DIR + list[ i ] ;
            BufferedReader br = null ;
            try {
                br = new BufferedReader( new InputStreamReader( new FileInputStream( file ) ) ) ;
                ReadIni.analisys( custom,br ) ;
                br.close() ;
                br = null ;
            } catch( Exception e ) {
                custom.clear() ;
                throw e ;
            } finally {
                if( br != null ) {
                    try {
                        br.close() ;
                    } catch( Exception e ) {
                    }
                }
            }
        }
    }
    
    /**
     * SHA1を生成.
     */
    private static final String getSha1() throws Exception {
        if( FileUtil.isDirExists( CUSTOM_TAGLIB_DIR ) == false ) {
            return null ;
        }
        String[] list = DirUtil.getDirByUseFileName( CUSTOM_TAGLIB_DIR,CUSTOM_TAGLIB_PLUS ) ;
        if( list == null || list.length <= 0 ) {
            return null ;
        }
        StringBuilder buf = new StringBuilder() ;
        int len = list.length ;
        for( int i = 0 ; i < len ; i ++ ) {
            if( i != 0 ) {
                buf.append( ":" ) ;
            }
            buf.append( list[ i ] ).append( "@" ).append( FileUtil.getLastTime( CUSTOM_TAGLIB_DIR + list[ i ] ) ) ;
        }
        byte[] bin = buf.toString().getBytes( "UTF8" ) ;
        buf = null ;
        return Digest.convert( "SHA1",bin ) ;
    }
    
}

