package org.itscool.commons.logging;

import java.io.File;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;

/**
 * ログクラスの抽象スーパークラスです
 * @author KANO
 * @since jdk1.4.1
 * @version 1.00A 2004/11/1
 */
public abstract class AbstractLog {
    
    /** ログ種別 ： トレース用 */
    public static final String TRACE = "trace";
    /** ログ種別　：　デバッグ用 */
    public static final String DEBUG = "debug";
    /** ログ種別 ： インフォメーション用 */
    public static final String INFO = "info";
    /** ログ種別 ： 警告用 */
    public static final String WARN = "warn";
    /** ログ種別 ： エラーログ出力 */
    public static final String ERROR = "error";
    /** ログ種別 ： エラーログ出力 */
    public static final String FATAL = "fatal";
    /** ログ出力モード：コンソール */
    public static final String DISP_MODE = "disp";
    /** ログ出力モード：ファイル */
    public static final String FILE_MODE = "file";
    /** ログ出力モード：コンソール/ファイル */
    public static final String ALLOUT_MODE = "all";
    
    protected boolean init;
    /**
     * ファイルの最大サイズ（KByte)
     */
    protected int maxSize;  //KByte
    
    /**
     * ファイル名を含めたパス
     */
    protected String logPath;
    
    /**
     * ログの出力レベル
     */
    protected String level = WARN;
    protected int levelInt = 2;
    
    /**
     * ログの出力モード
     */
    protected String mode = DISP_MODE;
    
    /**
     * 文字エンコーディング
     */
    protected String encode;
    
    /**
     * ログ出力対象クラス定義の一覧
     */
    protected List traces = new ArrayList();
    protected List debugs = new ArrayList();
    protected List infos = new ArrayList();
    protected List warns = new ArrayList();
    protected List errors = new ArrayList();
    protected List fatals = new ArrayList();
    
    /**
     * ログ出力対象クラス定義の一覧をセットします
     * @param infos ログ出力対象クラスの一覧
     */
    public void setInfos(List infos){
        this.infos = infos;
    }
    
    /**
     * ログ出力対象クラス定義の一覧を取得します
     * @return ログ出力対象クラスの一覧を返します
     */
    public List getInfos(){
        return infos;
    }
    
    
    /**
     * 指定されたクラス名がログ出力対象に合致するか判定します
     * @param clazzName 検査対象クラス
     * @return パターンにマッチする場合はtrueを返します
     */
    public boolean isOutputClass(String clazzName, String levelNow){
        boolean ret = false;
        if( isOutputClass(traces, clazzName) ){
            //ログの出力レベルのチェック
            if( 0 > getLevelNum(levelNow) ){
                return false;
            }else{
                return true;
            }
        }
        else if( isOutputClass(infos, clazzName) ){
            //ログの出力レベルのチェック
            if( 1 > getLevelNum(levelNow) ){
                return false;
            }else{
                return true;
            }
        }
        else if( isOutputClass(warns, clazzName) ){
            //ログの出力レベルのチェック
            if( 2 > getLevelNum(levelNow) ){
                return false;
            }else{
                return true;
            }
        }
        else if( isOutputClass(errors, clazzName) ){
            //ログの出力レベルのチェック
            if( 3 > getLevelNum(levelNow) ){
                return false;
            }else{
                return true;
            }
        }
        else if( isOutputClass(fatals, clazzName) ){
            //ログの出力レベルのチェック
            if( 4 > getLevelNum(levelNow) ){
                return false;
            }else{
                return true;
            }
        }
        //ログの出力レベルのチェック
        if( this.levelInt > getLevelNum(levelNow) ){
            return false;
        }else{
            return true;
        }
    }
    
    protected boolean isOutputClass(List levels, String clazzName){
        boolean ret = false;
        int size = levels.size();
        
        //ログ出力対象クラス定義がセットされていない場合はデフォルト
        if(size==0){
            return false;
        }
        
        //ログ出力対象クラスか走査
        for(int i=0; i<size; i++ ){
            String pattern = (String)levels.get(i);
            if(clazzName.indexOf(pattern) == 0 ){
                return true;
            }
        }
        
        return ret;
    }
    
    /**
     * ログレベルを取得します
     * @return level
     */
    public String getLevel() {
        return level;
    }
    /**
     * ログパスを取得します
     * @return logPath
     */
    public String getLogPath() {
        return logPath;
    }
    /**
     * ログのMAXサイズを取得します
     * @return maxSize
     */
    public int getMaxSize() {
        return maxSize;
    }
    /**
     * ログの出力モードを取得します
     * @return mode
     */
    public String getMode() {
        return mode;
    }
    
    public abstract void trace(String msg);
    
    public abstract void debug(String msg);
    
    public abstract void info(String msg);
    
    public abstract void warn(String msg);
    
    public abstract void error(String msg);
    
    public abstract void fatal(String msg);
    
    public abstract void trace(Exception msg);
    
    public abstract void debug(Exception msg);
    
    public abstract void info(Exception msg);
    
    public abstract void warn(Exception msg);
    
    public abstract void error(Exception msg);
    
    public abstract void fatal(Exception msg);
    
    public abstract void trace(Error msg);
    
    public abstract void debug(Error msg);
    
    public abstract void info(Error msg);
    
    public abstract void warn(Error msg);
    
    public abstract void error(Error msg);
    
    public abstract void fatal(Error msg);
    
    protected abstract void out(String logMsg, String levelNow, int classLevel);
    protected abstract void out(Exception logMsg, String levelNow);
    protected abstract void out(Error logMsg, String levelNow);
    
    /**
     * ログレベルの取得
     * @param levelNow ログレベル
     * @return ログレベルの名称を文字列で返します。
     */
    public static String getLogKind( String levelNow){
        if( levelNow.equals(TRACE)){
            return "DEBUG";
        }else if( levelNow.equals(INFO)){
            return "INFO ";
        }else if( levelNow.equals(WARN)){
            return "WORN ";
        }else if( levelNow.equals(ERROR)){
            return "ERROR";
        }else if( levelNow.equals(FATAL)){
            return "SYSTEM";
        }else{
            return "DEBUG";
        }
    }
    
    /**
     * ログ出力モードの取得
     * @param mode ログ出力モード
     * @return ログ出力モードの名称を文字列で返します。
     */
    public static String getLogMode( String mode){
        if( mode.equals(DISP_MODE)){
            return "DISP ONLY";
        }else if( mode.equals(FILE_MODE)){
            return "FILE ONLY";
        }else{
            return "DISP & FILE ";
        }
    }
    
    /**
     * 現在時刻の取得
     *
     * @return yyyy/mm/dd/ hh:mimi:ss:msmsmsms 形式の時刻
     */
    protected static String getNowDate(){
        //ミリ秒単位で表す。
        Calendar date = Calendar.getInstance();
        int year =date.get(Calendar.YEAR);
        int month = date.get(Calendar.MONTH) + 1;
        int day = date.get(Calendar.DATE);
        int hour = date.get(Calendar.HOUR_OF_DAY);
        int minute = date.get(Calendar.MINUTE);
        int second = date.get(Calendar.SECOND);
        String monthPadding="";
        String dayPadding = "";
        String hourPadding = "";
        String minutePadding = "";
        String secondPadding = "";
        if( month < 10 ){
            monthPadding = "0";
        }
        if( day < 10 ){
            dayPadding = "0";
        }
        if( hour < 10 ){
            hourPadding = "0";
        }
        if( minute < 10 ){
            minutePadding = "0";
        }
        if( second < 10 ){
            secondPadding ="0";
        }
        //String mSecond = String.valueOf(date.get(Calendar.MILLISECOND ));
        StringBuffer ret = new StringBuffer();
        ret.append(year).append("/");
        ret.append(monthPadding).append(month).append("/");
        ret.append(dayPadding).append(day).append(" ");
        ret.append(hourPadding).append(hour).append(":");
        ret.append(minutePadding).append(minute).append(":");
        ret.append(secondPadding).append(second );
        
        return ret.toString();
    }
    
    /**
     * 現在時刻の取得
     *
     * @return yyyymmddHHMMSS 形式の時刻
     */
    protected static String getNowDateYYYYMMDDHHMMSS(){
        //ミリ秒単位で表す。
        Calendar date = Calendar.getInstance() ;
        String year = String.valueOf(date.get(Calendar.YEAR));
        String month = String.valueOf(date.get(Calendar.MONTH));
        String day = String.valueOf(date.get(Calendar.DATE));
        String hour = String.valueOf(date.get(Calendar.HOUR));
        String minute = String.valueOf(date.get(Calendar.MINUTE));
        String second = String.valueOf(date.get(Calendar.SECOND ));
        return ( year + month + day + hour + minute + second );
    }
    
    /**
     * 現在日の取得
     *
     * @return yyyymmdd 形式の時刻
     */
    protected static String getNowDateYYYYMMDD(){
        //ミリ秒単位で表す。
        Calendar date = Calendar.getInstance() ;
        String year = String.valueOf(date.get(Calendar.YEAR));
        String month = String.valueOf(date.get(Calendar.MONTH));
        String day = String.valueOf(date.get(Calendar.DATE));
        
        return ( year + month + day );
    }
    
    /**
     * ログファイルのサイズチェック ログファイルが一定の大きさ以上
     * であればバックアップファイルに追加。
     * バックアップファイルが一定の大きさ以上であれば、バックアップ
     * を作成する。
     */
    protected synchronized void sizeCheck(){
        File file = new File( logPath  );
        
        if( file.canRead() ){
            //がmaxSizeを超える場合はバックアップファイルに残す。
            if( file.length() >= ( maxSize * 1024 ) ){
                file.renameTo( new File( logPath + "." + getNowDateYYYYMMDDHHMMSS() ));
            }
        }
        file = null;
    }
    /**
     * @return エンコード名を返します
     */
    public String getEncode() {
        return encode;
    }
    public void setEncode(String encode) {
        this.encode = encode;
    }
    public void setLevel(String level) {
        this.level = level;
        levelInt = getLevelNum(level);
    }
    protected int getLevelNum(String level) {
        this.level = level;
        if( level.equals(TRACE)){
            return 0;
        }else if( level.equals(DEBUG)){
            return 1;
        }else if( level.equals(INFO)){
            return 2;
        }else if( level.equals(WARN)){
            return 3;
        }else if( level.equals(ERROR)){
            return 4;
        }else if( level.equals(FATAL)){
            return 5;
        }else{
            return 1;
        }
    }
    public void setLogPath(String logPath) {
        this.logPath = logPath;
    }
    public void setMaxSize(int maxSize) {
        this.maxSize = maxSize;
    }
    public void setMode(String mode) {
        this.mode = mode;
    }
    public boolean isInit() {
        return init;
    }
    public void setInit(boolean init) {
        this.init = init;
    }

    public List getTraces() {
        return traces;
    }

    public void setTraces(List debugs) {
        this.traces = debugs;
    }

    public List getWarns() {
        return warns;
    }

    public void setWarns(List warns) {
        this.warns = warns;
    }

    public List getErrors() {
        return errors;
    }

    public void setErrors(List errors) {
        this.errors = errors;
    }

    public List getFatals() {
        return fatals;
    }

    public void setFatals(List fatals) {
        this.fatals = fatals;
    }

    public List getDebugs() {
        return debugs;
    }

    public void setDebugs(List debugs) {
        this.debugs = debugs;
    }
    
}
