package org.itscool.commons.logging;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;

/**
 * シンプルなロギングユーティリティクラスです<br>
 * このクラスは主に以下の機能を提供します。<br>
 * (1) ログローテイト<br>
 *    出力されるログファイルのサイズが指定サイズを超えると日付付の拡張子
 *    でログのバックアップを行います。<br>
 *    ファイルサイズはmaxSizeで指定します<br>
 * <br>
 * (2) ログレベルの切り分け<br>
 * 以下のレベルでログの出力レベルを切り替える事が出来ます。<br>
 * デバッグ：debug
 * 印フォーメーション：info<br>
 * 警告：warn<br>
 * エラー：error<br>
 * 重大なエラー：fatal<br>
 * <br>
 * (3) 出力モードの切り分け<br>
 *   以下のモードでログの出力モードを切り替える事ができます。<br>
 * コンソール出力：disp<br>
 * ファイル出力：file<br>
 * コンソール／ファイル出力：all<br>
 * <br>
 * (4) ファイル出力時のエンコードはencodeで指定します<br>
 * <br>
 * (5) ログ出力対象クラスのパターン定義<br>
 * outputClassesリストにログ出力対象クラスのパターンを定義します<br>
 * パターンを定義しない場合は全てのログが出力対象となります<br>
 *
 * @author KANO
 * @since jdk1.4.1
 * @version 1.00A 2000/03/02
 */
public class SimpleLog extends AbstractLog{
    private static SimpleLog log = null;
    
    public void trace(String msg){
        out(msg, TRACE, 3);
    }
    
    public void debug(String msg){
        out(msg, DEBUG, 3);
    }
    
    public void info(String msg){
        out(msg, INFO, 3);
    }
    
    public void warn(String msg){
        out(msg, WARN, 3);
    }
    
    public void error(String msg){
        out(msg, ERROR, 3);
    }
    
    public void fatal(String msg){
        out(msg, FATAL, 3);
    }
    
    public void trace(Exception msg){
        out(msg, TRACE);
    }
    
    public void debug(Exception msg){
        out(msg, DEBUG);
    }
    
    public void info(Exception msg){
        out(msg, INFO);
    }
    
    public void warn(Exception msg){
        out(msg, WARN);
    }
    
    public void error(Exception msg){
        out(msg, ERROR);
    }
    
    public void fatal(Exception msg){
        out(msg, FATAL);
    }
    
    public void trace(Error msg){
        out(msg, TRACE);
    }
    
    public void debug(Error msg){
        out(msg, DEBUG);
    }
    
    public void info(Error msg){
        out(msg, INFO);
    }
    
    public void warn(Error msg){
        out(msg, WARN);
    }
    
    public void error(Error msg){
        out(msg, ERROR);
    }
    
    public void fatal(Error msg){
        out(msg, FATAL);
    }
    
    /**
     * Logインスタンスを取得します
     * @return Logインスタンスを返します
     */
    public static synchronized AbstractLog getInstance(){
        if( log == null ){
            log = new SimpleLog();
        }
        return log;
    }
    
    /**
     * [yyyy/mm/dd/HH:MM:SS:mmmmmm]]level[LEVEL]class:[CLASS]method:[METHOD]msg[MSG]
     * 形式のログを出力します。<br>
     * outメソッド呼出もとのクラスはThrowable().getStackTrace()メソッドを使って
     * 呼出もとのクラス名とメソッド名を検索します<br>
     *
     * @param logMsg ログ
     * @param levelNow ログの出力レベル
     */
    protected synchronized void out(String logMsg, String levelNow, int classLevel){
        //例外出力時のクラス名を取得
        CallClass call = new CallClass(classLevel);  //3で固定！
        String clazz = call.getClassName();
        String func = call.getMethodName();
        int line = call.getLineNumber();
        
        //ログ出力対象でない場合は未出力
        if(!isOutputClass(clazz, levelNow)){
            return;
        }
//        //ログの出力レベルのチェック
//        if( this.levelInt > getLevelNum(levelNow) ){
//            return;
//        }
        try{
            if( mode.equals(FILE_MODE) ){
                outFile( clazz, func, logMsg, levelNow, line );
            }else if( mode.equals(DISP_MODE) ){
                outDisp( clazz, func, logMsg, levelNow, line );
            }else if( mode.equals(ALLOUT_MODE)){
                outFile( clazz, func, logMsg, levelNow, line );
                outDisp( clazz, func, logMsg, levelNow, line );
            }else{
                outDisp( clazz, func, logMsg, levelNow, line );
            }
        }catch( IOException ioe ){
            throw new LogRuntimeException(ioe.getMessage());
        }
    }
    
    /**
     * [yyyy/mm/dd/HH:MM:SS:mmmmmm]]level[LEVEL]class:[CLASS]method:[METHOD]msg[MSG]
     * 形式のログを出力します。<br>
     * outメソッド呼出もとのクラスはThrowable().getStackTrace()メソッドを使って
     * 呼出もとのクラス名とメソッド名を検索します<br>
     *
     * @param e 例外
     * @param levelNow ログの出力レベル
     */
    protected synchronized void out(Exception e, String levelNow){
        if(e.getMessage()==null){
            log.out(e.getClass().getName(), levelNow, 4);
        }else{
            log.out(e.getMessage(), levelNow, 4);
        }
        StackTraceElement[] traceList = e.getStackTrace();
        for (int i = 0; i < traceList.length; i++) {
            StackTraceElement trace = traceList[i];
            out(trace.toString(), levelNow, 4);
        }
    }
    
    /**
     * [yyyy/mm/dd/HH:MM:SS:mmmmmm]]level[LEVEL]class:[CLASS]method:[METHOD]msg[MSG]
     * 形式のログを出力します。<br>
     * outメソッド呼出もとのクラスはThrowable().getStackTrace()メソッドを使って
     * 呼出もとのクラス名とメソッド名を検索します<br>
     *
     * @param e 例外
     * @param levelNow ログの出力レベル
     */
    protected synchronized void out(Error e, String levelNow){
        if(e.getMessage()==null){
            log.out(e.getClass().getName(), levelNow, 4);
        }else{
            log.out(e.getMessage(), levelNow, 4);
        }
        StackTraceElement[] traceList = e.getStackTrace();
        for (int i = 0; i < traceList.length; i++) {
            StackTraceElement trace = traceList[i];
            out(trace.toString(), levelNow, 4);
        }
    }
    
//    /**
//     * [yyyy/mm/dd/HH:MM:SS:mmmmmm]]level[LEVEL]class:[CLASS]method:[METHOD]msg[MSG]
//     * 形式のログをINFOレベルで出力します。<br>
//     * outメソッド呼出もとのクラスはThrowable().getStackTrace()メソッドを使って
//     * 呼出もとのクラス名とメソッド名を検索します<br>
//     *
//     * @param logMsg ログ
//    */
//    public static void outConsole(String logMsg){
//        CallClass call = new CallClass(1);
//        String clazz = call.getClassName();
//        String func = call.getMethodName();
//
//        outDisp( clazz, func, logMsg, Log.INFO );
//    }
    
    /**
     * ログをファイルに出力します。
     *
     * @param clazz outメソッドを呼出したクラス名
     * @param func outメソッドを呼び出したメソッド名
     * @param logMsg ログ
     * @param levelNow ログの出力レベル
     */
    protected void outFile(String clazz, String func, String logMsg, String levelNow, int line)
    throws IOException{
        if( maxSize <= 0 ){
            outDisp( clazz, func, logMsg, levelNow, line );
            outDisp( SimpleLog.class.getName(), "outFile()", "ファイルの出力サイズが０です", ERROR, line );
            return;
        }
        
        //ログファイルのサイズチェック
        sizeCheck();
        //ファイルオープン
        //RandomAccessFile raf = new RandomAccessFile( logPath , "rw" );
        //ファイルの最後へ移動
        //raf.seek( raf.length());
        //メッセージの書き込み
        //raf.writeBytes(Log.getMsg( clazz, func, logMsg, levelNow));
        //ファイルクローズ
        //raf.close();
        Writer w = new OutputStreamWriter(new FileOutputStream(logPath, true), encode);
        w.write(SimpleLog.getMsg( clazz, func, logMsg, levelNow, line));
        w.close();
    }
    
    /**
     * ログをコンソールに出力します。
     *
     * @param clazz outメソッドを呼出したクラス名
     * @param func outメソッドを呼び出したメソッド名
     * @param logMsg ログ
     * @param levelNow ログの出力レベル
     */
    protected static void outDisp(String clazz, String func, String logMsg,
        String levelNow, int line){
        
        System.out.print(getMsg(clazz, func, logMsg, levelNow, line));
    }
    
    /**
     * メッセージログの取得
     * @param clazz    プロセス名称
     * @param func     関数名称
     * @param logMsg   ログメッセージ
     * @param levelNow ログ出力レベル
     * @return log     作成後のログメッセージ
     *
     */
    protected static String getMsg( String clazz, String func, String logMsg,
        String levelNow, int line ){
        StringBuffer log = new StringBuffer();
        log.append(SimpleLog.getNowDate());
        log.append(" ").append(getLogKind(levelNow));
        log.append(" ").append(clazz);
        log.append(" ").append(func);
        log.append("(line=").append(line).append(")");
        log.append(" (").append(logMsg).append(")");
        log.append(System.getProperty( "line.separator" ));
        return log.toString();
    }
}

