/**
 * Copyright (C) 2006-2007  NTT DATA CORPORATION
 * 
 * Version: 1.0.0 2007/04/01
 *  
 */
package net.cellcomputing.himawari.util;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.DateFormat;
import java.util.Date;
import java.util.logging.ConsoleHandler;
import java.util.logging.Level;
import java.util.logging.LogManager;
import java.util.logging.Logger;

/**
 *　Logを管理するクラス
 *　LoggerをもつSingletonクラス
 *
 *  getLoggerを用いてHimawariLoggerを取得する。
 *  出力したいレベルはsetLogLevelで設定を行う
 * @author NTT DATA Corporation
 */
public class HimawariLogger {
	/**
	 * DEBUGレベル文字列
	 */
	public static final String LEVEL_DEBUG = "DEBUG";
	/**
	 * INFOレベル文字列
	 */
	public static final String LEVEL_INFO = "INFO";
	/**
	 * WARNINGレベル文字列
	 */
	public static final String LEVEL_WARNING = "WARNING";
	/**
	 * CRITICALレベル文字列
	 */
	public static final String LEVEL_CRITICAL = "CRITICAL";
	/**
	 * ERRORレベル文字列
	 */
	public static final String LEVEL_ERROR = "ERROR";
	/**
	 * loggerの名前
	 */
	private static final String LOGGER_NAME = "net.cellcomputing.himawari";
	private Logger logger;
	private ConsoleHandler handler;
	private static HimawariLogger logInstance;
	
	/**
	 * StackTraceを保存するファイル名
	 */
	private static final String LOG_FILE_NAME = "stacktrace.log";
	private static final DateFormat df = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM);
	

	/**
	 * エラーメッセージで使用される文字定数
	 */
	private final String REPEATED_MESSAGE = " Last message repeated ";
	private final String REPEATED_TIMES = " time";
	private final String DEFALUT_MESSAGE = "";
	
	
	/**
	 * 直前に指定されたメッセージ
	 */
	private String lastMessage = DEFALUT_MESSAGE;

	
	/**
	 * 重複メッセージの回数
	 */
	private int	count = 0;
	
	/**
	 * 直前に指定されたログレベル
	 */
	private Level lastLevel = Level.OFF;
	
	/**
	 * ログ用の時刻フォーマット
	 */
	private Date date;

	
	/**
	 * コンストラクタ
	 * Loggerの設定をする
	 */
	private HimawariLogger(){
		if(logger == null){
			LogManager.getLogManager().reset();
			logger = Logger.getLogger(LOGGER_NAME);
		
			handler = new ConsoleHandler();
			handler.setFormatter(new HimawariLogFormatter());
			
			logger.addHandler(handler);
			
		}
		
	}
	
	/**
	 * ログを取得するレベルを設定する
	 * 
	 * @param level 設定するレベル
	 */
	public void setLogLevel(Level level){
		logger.setLevel(level);
		handler.setLevel(level);
	}
	
	public void setLogLevel(String level){
		//引数に応じてログのレベルを設定する
		
		if(level.equals(LEVEL_CRITICAL)){
			logger.setLevel(Level.SEVERE);
			handler.setLevel(Level.SEVERE);
		}else if(level.equals(LEVEL_ERROR)){
			logger.setLevel(Level.WARNING);
			handler.setLevel(Level.WARNING);
		}else if(level.equals(LEVEL_WARNING)){
			logger.setLevel(Level.INFO);
			handler.setLevel(Level.INFO);
		}else if(level.equals(LEVEL_INFO)){
			logger.setLevel(Level.CONFIG);
			handler.setLevel(Level.CONFIG);
		}else if(level.equals(LEVEL_DEBUG)){
			logger.setLevel(Level.FINE);
			handler.setLevel(Level.FINE);
		}
	}
	
	/**
	 * ログレベルを返す
	 * @return ログレベル
	 */
	public Level getLevel(){
		return logger.getLevel();
	}
	/**
	 * ログを管理するLoggerを返す
	 * 
	 * @return logger
	 */
	public static HimawariLogger getLogger(){
		
		if(logInstance == null){
			logInstance =new HimawariLogger();
		}
		
		return logInstance;
	}
	
	/**
	 * 同じメッセージが繰り返しLoggerに渡されているかどうかを判定する。
	 * 直前のメッセージが同じLevelかつ同じメッセージであればtrueを返し、
	 * countを1増やす。
	 * もしfalseであれば、flushLoggerを呼び出した後
	 * lastMessageとLevelを引数で指定されたものに変更し、
	 * countを0に戻す。
	 * 
	 * @param str ログメッセージ
	 * @param level ログのレベル
	 * @return 直前のメッセージが同じであり、同等のLevelであればtrue,それ以外はfalse
	 */
	private boolean checkRepeatedMessage(String str,Level level){
		if(lastLevel.equals(level)){
			if(str.equals(lastMessage)){
				count++;
				return true;
			}			
		}
		//flushする。

		flushLogger();
		lastMessage = str;
		count = 0;
		lastLevel = level;
		
		return false;
	}
	
	/**
	 * Aqsisのinfoレベルの文字列を出力する
	 * 
	 * @param str メッセージ
	 */
	public void info(String str){
		//重複でない場合はlogをflushして表示する
		if(!checkRepeatedMessage(str,Level.CONFIG))
			logger.config(str);
	}
	/**
	 * Aqsisのcriticalレベルの文字列を出力する
	 * 
	 * @param str メッセージ
	 */
	
	public void critical(String str){
		//重複でない場合はlogをflushして表示する
		if(!checkRepeatedMessage(str,Level.SEVERE))
			logger.severe(str);
	}
	/**
	 * AqsisのERRORレベルの文字列を出力する
	 * 
	 * @param str メッセージ
	 */
	public void error(String str){
		//重複でない場合はlogをflushして表示する
		if(!checkRepeatedMessage(str,Level.WARNING))
			logger.warning(str);
	}
	/**
	 * AqsisのWARNINGレベルの文字列を出力する
	 * 
	 * @param str メッセージ
	 */
	public void warning(String str){
		//重複でない場合はlogをflushして表示する
		if(!checkRepeatedMessage(str,Level.INFO))
			logger.info(str);
	}
	/**
	 * AqsisのDEBUGレベルの文字列を出力する
	 * 
	 * @param str メッセージ
	 */
	public void debug(String str){
		//重複でない場合はlogをflushして表示する
		if(!checkRepeatedMessage(str,Level.FINE))
			logger.fine(str);
	}
	
	
	private static File logFile;
	
	/**
	 * 
	 * ExceptionのStackTraceをファイルに記録する。
	 * 
	 * @param e	スローされたException
	 */
	public static void outputException( Exception e ){
		
		//ファイルオープン
		if( logFile == null ){
			logFile = new File( LOG_FILE_NAME );
			logFile.delete();
		}
		
		//ライターを作成
		PrintWriter writer;
		try {
			writer = new PrintWriter( new FileWriter(logFile,true) );
		} catch (IOException e1) {
			logInstance = getLogger();
			logInstance.error("log file open error. file is " +LOG_FILE_NAME+ "\n");
			return;
		}
		
		// for DEBUG
//		writer = new PrintWriter( System.err );
		
//		//日付を出力
//		Date date = new Date();
//		writer.println( df.format(date) + " Threw Exceptiln is..." );
//		e.printStackTrace( writer );
//		
//		Throwable cause = e.getCause();
//		if( cause!=null ){
//			writer.print( "Caused by: " );
//			cause.printStackTrace( writer );
//		}
//		writer.close();
		
		//StackTraceの内容を取得
		StackTraceElement[] elements = e.getStackTrace();
		
		//エラーメッセージの作成
		Date date = new Date();
		writer.println( df.format(date) + " Threw Exceptiln is..." );
		
		writer.println( e );
		for( StackTraceElement ele : elements ){
			writer.print( "\tat " );
			writer.println( ele );
		}
		
		Throwable cause = e.getCause();
		if( cause!=null ){
			writer.print("Caused by: ");
			cause.printStackTrace( writer );
		}
		
		//ファイルクローズ
		writer.close();
		
	}
	
	/**
	 * メッセージをflushし、出力する。
	 */
	public void flushLogger(){
		if(count > 0){
			date = new Date();
			StringBuffer mes = new StringBuffer();
			mes.append(df.format(date));
			mes.append(REPEATED_MESSAGE);
			mes.append(count);
			mes.append(REPEATED_TIMES);

			if(count > 1){
				mes.append("s");
			}
			if(logger.isLoggable(lastLevel))
				System.err.println(mes.toString());
			
			lastMessage = DEFALUT_MESSAGE;
			count = 0;
			lastLevel = Level.OFF;
		}
	}
}
