package jp.agentec.abook.abv.bl.common.log;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintStream;
import java.util.Date;

import jp.agentec.abook.abv.bl.common.ABVEnvironment;
import jp.agentec.adf.util.DateTimeFormat;
import jp.agentec.adf.util.DateTimeUtil;
import jp.agentec.adf.util.FileUtil;

public class Logger extends AbstractLogger {
	private static ABVEnvironment env = ABVEnvironment.getInstance();
	private static AbstractLogger logger = new Logger();
	private static AbstractLogger logger2;
	private PrintStream ps = System.out;
	private String logPathFormat;
	private String currentDay;
	private int retentionPeriod = 1;
	
	public Logger() {
	}
	
	public static AbstractLogger getLogger() {
		return logger;
	}
	
	public void setLogPathFormat(String logPathFormat) {
		this.logPathFormat = logPathFormat;
	}
	
	public static void setLogger(AbstractLogger abstractLogger) {
		logger = abstractLogger;
	}

	public static void setLogger2(AbstractLogger abstractLogger) {
		logger2 = abstractLogger;
	}

	public void setRetentionPeriod(int retentionPeriod) {
		this.retentionPeriod = retentionPeriod;
	}

	public static void v(String log) {
		v(env.defaultLogName, log);
	}

	public static void v(String tag, String log) {
		if (isVerboseEnabled()) {
			logger.verbose(tag, log);
			if (logger2 != null) {
				logger2.verbose(tag, log);
			}
		}
	}

	public static void v(String tag, String log, Object... param) {
		if (isVerboseEnabled()) {
			v(tag, String.format(log, param));
		}
	}

	public static void v(String log, Throwable t) {
		v(env.defaultLogName, log, t);
	}
	
	public static void v(String tag, String log, Throwable t) {
		if (isVerboseEnabled()) {
			logger.verbose(tag, log, t);
			if (logger2 != null) {
				logger2.verbose(tag, log, t);
			}
		}
	}

	public static void d(String log) {
		d(env.defaultLogName, log);
	}
	
	public static void d(String tag, String log) {
		if (isDebugEnabled()) {
			logger.debug(tag, log);
			if (logger2 != null) {
				logger2.debug(tag, log);
			}
		}
	}
	
	public static void d(String tag, String log, Object... param) {
		if (isDebugEnabled()) {
			d(tag, String.format(log, param));
		}
	}
	
	public static void d(String log, Throwable t) {
		d(env.defaultLogName, log, t);
	}
	
	public static void d(String tag, String log, Throwable t) {
		if (isDebugEnabled()) {
			logger.debug(tag, log, t);
			if (logger2 != null) {
				logger2.debug(tag, log, t);
			}
		}
	}
	
	public static void i(String log) {
		i(env.defaultLogName, log);
	}
	
	public static void i(String tag, String log) {
		if (isInfoEnabled()) {
			logger.info(tag, log);
			if (logger2 != null) {
				logger2.info(tag, log);
			}
		}
	}
	
	public static void i(String tag, String log, Object... param) {
		if (isInfoEnabled()) {
			i(tag, String.format(log, param));
		}
	}
	
	public static void i(String log, Throwable t) {
		i(env.defaultLogName, log, t);
	}
	
	public static void i(String tag, String log, Throwable t) {
		if (isInfoEnabled()) {
			logger.info(tag, log, t);
			if (logger2 != null) {
				logger2.info(tag, log, t);
			}
		}
	}
	
	public static void w(String log) {
		w(env.defaultLogName, log);
	}
	
	public static void w(String tag, String log) {
		if (isWarnEnabled()) {
			logger.warn(tag, log);
			if (logger2 != null) {
				logger2.warn(tag, log);
			}
		}
	}
	
	public static void w(String tag, String log, Object... param) {
		if (isWarnEnabled()) {
			w(tag, String.format(log, param));
		}
	}
	
	public static void w(String log, Throwable t) {
		w(env.defaultLogName, log, t);
	}
	
	public static void w(String tag, String log, Throwable t) {
		if (isWarnEnabled()) {
			logger.warn(tag, log, t);
			if (logger2 != null) {
				logger2.warn(tag, log, t);
			}
		}
	}
	
	public static void e(String log) {
		e(env.defaultLogName, log);
	}
	
	public static void e(String tag, String log) {
		if (isErrorEnabled()) {
			logger.error(tag, log);
			if (logger2 != null) {
				logger2.error(tag, log);
			}
		}
	}
	
	public static void e(String tag, String log, Object... param) {
		if (isErrorEnabled()) {
			e(tag, String.format(log, param));
		}
	}
	
	public static void e(String log, Throwable t) {
		e(env.defaultLogName, log, t);
	}
	
	public static void e(String tag, String log, Throwable t) {
		if (isErrorEnabled()) {
			logger.error(tag, log, t);
			if (logger2 != null) {
				logger2.error(tag, log, t);
			}
		}
	}

    /**
     * #26344 端末ログファイル削除方式改善のため
     */
    private boolean isDeletable(File file) {
        long toTime = retentionPeriod  * 24 * 60 * 60 * 1000; // [retentionPeriod] 日
        long fromTime = (retentionPeriod + 7)  * 24 * 60 * 60 * 1000; // [retentionPeriod + 7] 日
        // fromTime < file.lastModified() < toTime
        if (file.lastModified() > System.currentTimeMillis() - fromTime && file.lastModified() < System.currentTimeMillis() -  toTime) {
            return true;
        }
        // ファイルのサイズが5Mを超えている場合
        if (file.lastModified() < System.currentTimeMillis() - fromTime && file.length() > 5 * 1024 * 1024) {
            return true;
        }
        return false;
    }

    private void log(String level, String tag, String log) {
        Date date = new Date();
        String day = DateTimeUtil.toString(date, DateTimeFormat.MMdd_none);
        if (logPathFormat != null) {
            if (currentDay == null || !currentDay.equals(day)) {
                File dir = new File(FileUtil.getParentPath(logPathFormat));
                if (dir.exists()) {
                    File[] files = dir.listFiles();
                    if (files != null) {
                        for (File file : files) { // 同一ディレクトリの旧ファイル（retentionPeriod日前）をすべて削除する
//                            if (file.lastModified() < System.currentTimeMillis() - retentionPeriod * 24 * 60 * 60 * 1000) {
                            if (isDeletable(file)) {
                                file.delete();

                            }
                        }
                    }
                }
                currentDay = day;
                try {
                    ps = new PrintStream(new FileOutputStream(String.format(logPathFormat, day), true));
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                }
            }
        }

        String time = DateTimeUtil.toString(date, DateTimeFormat.MMddHHmmssSSS_hyphen);
        ps.println(time + ": [" + level + "] " + tag + "(" + Thread.currentThread().getId() + "): " + log);
    }

	private void log(String level, String defaultLogName, String log, Throwable e) {
		log(level, defaultLogName, log);
		e.printStackTrace(ps);
	}

	@Override
	protected void verbose(String tag, String log) {
		log("VERBOSE", tag, log);
	}

	@Override
	protected void verbose(String tag, String log, Throwable t) {
		log("VERBOSE", tag, log, t);
	}

	@Override
	protected void debug(String tag, String log) {
		log("DEBUG", tag, log);
	}

	@Override
	protected void debug(String tag, String log, Throwable t) {
		log("DEBUG", tag, log, t);
	}

	@Override
	protected void info(String tag, String log) {
		log("INFO", tag, log);
	}

	@Override
	protected void info(String tag, String log, Throwable t) {
		log("INFO", tag, log, t);
	}

	@Override
	protected void warn(String tag, String log) {
		log("WARN", tag, log);
	}

	@Override
	protected void warn(String tag, String log, Throwable t) {
		log("WARN", tag, log, t);
	}

	@Override
	protected void error(String tag, String log) {
		log("ERROR", tag, log);
	}

	@Override
	protected void error(String tag, String log, Throwable t) {
		log("ERROR", tag, log, t);
	}

	private static boolean isLoggable(int level) {
		return env.getLogLevel().level() <= level;
	}

	public static boolean isVerboseEnabled() {
		return isLoggable(LogLevel.verbose.level());
	}
	
	public static boolean isDebugEnabled() {
		return isLoggable(LogLevel.debug.level());
	}
	
	public static boolean isInfoEnabled() {
		return isLoggable(LogLevel.info.level());
	}
	
	public static boolean isWarnEnabled() {
		return isLoggable(LogLevel.warn.level());
	}
	
	public static boolean isErrorEnabled() {
		return isLoggable(LogLevel.error.level());
	}
	
}
