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

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

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.maachang.comet.MaachangDef;
import org.maachang.conf.Config;
import org.maachang.conf.ReadIni;
import org.maachang.util.FileUtil;
import org.maachang.util.StringUtil;

/**
 * cron.conf用読み込みファイル.
 *
 * @version 2008/07/01
 * @author masahito suzuki
 * @since MaachangComet 1.1F
 */
public class CronConfig {
    
    /**
     * LOG.
     */
    private static final Log LOG = LogFactory.getLog( CronConfig.class ) ;
    
    /**
     * 読み込みファイル名.
     */
    private static final String READ_FILE = MaachangDef.DIRECTORY_CONFIG+"cron.conf" ;
    
    /**
     * チェック間隔.
     */
    private static final long CHECK_TIMING = 15000L ;
    
    /**
     * キャラクタセット.
     */
    private static final String CHARSET = "UTF8" ;
    
    /**
     * 1Cron区切り長.
     */
    private static final int CRON_LINE = 6 ;
    
    /**
     * 読み込み対象データ.
     */
    private Config iniParams = null ;
    
    /**
     * 対象ファイル.
     */
    private String fileName = null ;
    
    /**
     * 最終更新日.
     */
    private long lastUpdate = -1L ;
    
    /**
     * 最終確認時間.
     */
    private long lastCheckTime = -1L ;
    
    /**
     * Cron実行情報.
     */
    private ArrayList<CronBean> manager = null ;
    
    /**
     * コンストラクタ.
     */
    public CronConfig() {
        
    }
    
    /**
     * オープン.
     * <BR><BR>
     * オープンします.
     * <BR>
     * @exception Exception 例外.
     */
    public synchronized void open() throws Exception {
        this.open( READ_FILE ) ;
    }
    
    /**
     * オープン.
     * <BR><BR>
     * オープンします.
     * <BR>
     * @param name 対象のファイル名を設定します.
     * @exception Exception 例外.
     */
    public synchronized void open( String name ) throws Exception {
        this.fileName = FileUtil.getFullPath( name ) ;
        try {
            this.updateToReload() ;
        } catch( Exception e ) {
            this.close() ;
            throw e ;
        } finally {
        }
    }
    
    /**
     * クローズ.
     * <BR><BR>
     * クローズ処理.
     */
    public synchronized void close() {
        manager = null ;
        iniParams = null ;
        fileName = null ;
        lastUpdate = -1L ;
        lastCheckTime = -1L ;
    }
    
    /**
     * 文字列に変換.
     */
    public synchronized String toString() {
        if( iniParams != null ) {
            return iniParams.toString() ;
        }
        return "null" ;
    }
    
    /**
     * CronBean数を取得.
     * @param out 更新結果を返すBoolean配列を設定します.
     * @return int CronBean数が返されます.
     */
    public synchronized int size( boolean[] out ) {
        if( manager != null ) {
            boolean res = updateToReload() ;
            if( out != null && out.length > 0 ) {
                out[ 0 ] = res ;
            }
            return manager.size() ;
        }
        return 0 ;
    }
    
    /**
     * 指定位置のCronBean情報を取得.
     * @param out 更新結果を返すBoolean配列を設定します.
     * @param no 対象の項番を設定します.
     * @return CronBean 対象のCronBean情報が返されます.
     */
    public synchronized CronBean get( boolean[] out,int no ) {
        if( manager != null ) {
            boolean res = updateToReload() ;
            if( out != null && out.length > 0 ) {
                out[ 0 ] = res ;
            }
            if( no <= -1 || no >= manager.size() ) {
                return null ;
            }
            return manager.get( no ) ;
        }
        return null ;
    }
    
    /**
     * 更新時には、Reload処理を行います.
     */
    private boolean updateToReload() {
        boolean ret = false ;
        if( lastCheckTime + CHECK_TIMING <= System.currentTimeMillis() ) {
            try {
                long tm = FileUtil.getLastTime( fileName ) ;
                if( lastUpdate == -1 || lastUpdate != tm ) {
                    //LOG.info( "* * * Cronコンフィグ読み込み開始" ) ;
                    this.reload() ;
                    this.createConfigByCronBeans() ;
                    //LOG.info( "* * * Cronコンフィグ読み込み正常終了[" + manager.size() + "]件" ) ;
                    ret = true ;
                }
            } catch( Exception e ) {
                LOG.error( "* * * Cronコンフィグ読み込み失敗",e ) ;
            }
            lastCheckTime = System.currentTimeMillis() ;
        }
        return ret ;
    }
    
    /**
     * 再読み込み.
     */
    private final void reload()
        throws Exception {
        BufferedReader buf = null ;
        if( FileUtil.isFileExists( fileName ) == true ) {
            try {
                buf = new BufferedReader(
                    new InputStreamReader(
                        new FileInputStream( fileName ),CHARSET ) ) ;
                Config iniParams = new Config() ;
                ReadIni.analisys( iniParams,buf ) ;
                buf.close() ;
                buf = null ;
                long last = FileUtil.getLastTime( fileName ) ;
                // 読み込みデータを設定.
                this.iniParams = iniParams ;
                this.lastUpdate = last ;
            } catch( Exception e ) {
                throw e ;
            } finally {
                if( buf != null ) {
                    try {
                        buf.close() ;
                    } catch( Exception e ) {
                    }
                }
            }
        }
    }
    
    /**
     * Cron実行内容を生成.
     */
    private final void createConfigByCronBeans() throws Exception {
        manager = new ArrayList<CronBean>() ;
        if( iniParams == null || iniParams.isSection( "cron-tab" ) == false ) {
            return ;
        }
        int len = iniParams.size( "cron-tab","cron" ) ;
        for( int i = 0 ; i < len ; i ++ ) {
            String s = iniParams.get( "cron-tab","cron",i ) ;
            ArrayList<String> one = StringUtil.getCsv( false,s,"," ) ;
            if( one == null ) {
                LOG.warn( "Cron内容[no" + (i+1) + "]の解析に失敗しました" ) ;
                continue ;
            }
            int lenJ = one.size() ;
            if( lenJ != CRON_LINE ) {
                LOG.warn( "Cron内容[no" + (i+1) + " data:" + s + "]のパラメータ条件は不正です("+lenJ+"/"+CRON_LINE+")" ) ;
                continue ;
            }
            CronBean bean = new CronBean() ;
            try {
                for( int j = 0 ; j < lenJ ; j ++ ) {
                    String ss = one.get( j ) ;
                    if( ss == null || ( ss = ss.trim() ).length() <= 0 || "*".equals( ss ) ) {
                        continue ;
                    }
                    if( ss.startsWith( "(" ) && ss.endsWith( ")" ) ) {
                        String tm = ss.substring( 1,ss.length() -1 ) ;
                        tm = tm.trim() ;
                        int cd = Integer.parseInt( tm ) ;
                        if( cd <= 0 ) {
                            cd = -1 ;
                        }
                        switch( j ) {
                            case 0 :
                                bean.setMinuteTm( cd ) ;
                                break ;
                            case 1 :
                                bean.setHourTm( cd ) ;
                                break ;
                            case 2 :
                                bean.setDateTm( cd ) ;
                                break ;
                            case 3 :
                            case 4 :
                            case 5 :
                                bean = null ;
                                LOG.warn( "Cron内容[no" + (i+1) + " data:" + s + "]の月,曜日,スクリプト内容は条件指定ができません" ) ;
                                break ;
                        }
                    }
                    else {
                        switch( j ) {
                            case 0 :
                                bean.setMinute( Integer.parseInt( ss ) ) ;
                                break ;
                            case 1 :
                                bean.setHour( Integer.parseInt( ss ) ) ;
                                break ;
                            case 2 :
                                bean.setDate( Integer.parseInt( ss ) ) ;
                                break ;
                            case 3 :
                                bean.setMonth( Integer.parseInt( ss ) ) ;
                                break ;
                            case 4 :
                                bean.setWeek( Integer.parseInt( ss ) ) ;
                                break ;
                            case 5 :
                                bean.setScript( ss ) ;
                                break ;
                        }
                    }
                    if( bean == null ) {
                        break ;
                    }
                }
            } catch( Exception e ) {
                LOG.warn( "Cron内容[no" + (i+1) + " data:" + s + "]の解析中にエラーが発生しました",e ) ;
                bean = null ;
            }
            if( bean != null ) {
                if( bean.getScript() == null ) {
                    LOG.warn( "Cron内容[no" + (i+1) + " data:" + s + "]に対して、実行スクリプトが設定されていません" ) ;
                    bean = null ;
                    continue ;
                }
                else if( bean.getMinute() <= -1 && bean.getMinuteTm() <= -1 &&
                    bean.getHour() <= -1 && bean.getHourTm() <= -1 &&
                    bean.getDate() <= -1 && bean.getDateTm() <= -1 &&
                    bean.getMonth() <= -1 && bean.getWeek() <= -1 ) {
                    LOG.warn( "Cron内容[no" + (i+1) + " data:" + s + "]に対して、実行可能条件が設定されていません" ) ;
                    bean = null ;
                    continue ;
                }
                manager.add( bean ) ;
            }
        }
    }
}

