/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package textkeymatcher.util.log;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.nio.charset.Charset;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.logging.ErrorManager;
import java.util.logging.Formatter;
import java.util.logging.Handler;
import java.util.logging.LogManager;
import java.util.logging.LogRecord;
import java.util.logging.SimpleFormatter;
import org.jdesktop.application.Application;
import org.jdesktop.application.ApplicationContext;
import org.jdesktop.application.LocalStorage;

/**
 *
 * @author seraphy
 */
public class ApplicationLogHandler extends Handler {
    
    /**
     * ログディレクトリ
     */
    public static final String LOGDIR = "log";
    
    /**
     * プロパティファイルで指定されるログディレクトリのキー名
     */
    public static final String PROPERTY_LOGDIR = ".logDir";
    
    /**
     * ログファイルに使用する拡張子
     */
    public static final String LOGFILE_EXT = ".log";
    
    /**
     * ログの期限切れまでのデフォルト日数
     */
    public static final int DEFAULT_EXPIRES = 10;
    
    /**
     * ログ書き込み先、クローズされているか、まだ使われていなければnull
     */
    private Writer pw;
    
    /**
     * 排他制御用
     */
    private final Object lock = new Object();
    
    
    /**
     * 既定のディレクトリ上に日付形式でログファイルを作成する.
     */
    public ApplicationLogHandler() {
        setFormatter(new SimpleFormatter());
    }
    
    public static File getLogDir() {
        // アプリケーション設定ディレクトリ
        Application application = Application.getInstance();
        ApplicationContext context = application.getContext();
        LocalStorage localStorage = context.getLocalStorage();
        File appDataDir = localStorage.getDirectory();

        // ログディレクトリのプロパティによる指定があれば、それを使う.
        // なければデフォルト
        String logDirName = LogManager.getLogManager().getProperty(PROPERTY_LOGDIR);
        if (logDirName == null || logDirName.trim().length() == 0) {
            logDirName = LOGDIR;
        }
        
        return new File(appDataDir, logDirName.trim());
    }
    
    /**
     * ログファイルが未作成であれば作成する.
     * 作成できない場合は、ログファイルはnullのまま.
     */
    protected void ensureLogWriter() {
        synchronized (lock) {
            if (pw != null) {
                return;
            }
            File logFile = null;
            try {
                // ログディレクトリ
                File logDir = getLogDir();
                if ( !logDir.exists()) {
                    logDir.mkdirs();
                }

                // ログファイル
                logFile = new File(logDir, getCurrentTimeForFileName() + LOGFILE_EXT);

                // ログファイルを作成する.
                String enc = getEncoding();
                Charset cs;
                if (enc == null || enc.trim().length() == 0) {
                    cs = Charset.defaultCharset();
                } else {
                    cs = Charset.forName(enc);
                }
                OutputStreamWriter fileWriter = new OutputStreamWriter(new FileOutputStream(logFile), cs);
                pw = new BufferedWriter(fileWriter);

            } catch (Exception ex) {
                reportError("ApplicationLogHandler creation failed. " + logFile, ex, ErrorManager.OPEN_FAILURE);
                pw = null;
            }
        }
    }
    
    @Override
    public void close() {
        synchronized (lock) {
            if (pw != null) {
                try {
                    pw.close();
                    
                } catch (Exception ex) {
                    reportError("log close failed.", ex, ErrorManager.CLOSE_FAILURE);
                }
                pw = null;
            }
        }
    }

    @Override
    public void flush() {
        synchronized (lock) {
            if (pw != null) {
                try {
                    pw.flush();

                } catch (Exception ex) {
                    reportError("log flush failed.", ex, ErrorManager.FLUSH_FAILURE);
                }
            }
        }
    }

    @Override
    public void publish(LogRecord lr) {
        if (lr == null) {
            return;
        }
        
        synchronized (lock) {
            // ログファイルの準備
            ensureLogWriter();
            if (pw == null) {
                return;
            }
            
            // メッセージのフォーマット
            String msg = null;
            try {
                Formatter formatter = getFormatter();
                if (formatter != null) {
                    msg = formatter.format(lr);
                } else {
                    msg = lr.getMessage();
                }
            } catch (Exception ex) {
                reportError("log format failed.", ex, ErrorManager.FORMAT_FAILURE);
            }

            // ログファイルへの書き込み
            try {
                pw.write(msg);

                // すぐに確認できるようにフラッシュする
                pw.flush();
                
            } catch (Exception ex) {
                reportError("log write failed." + msg, ex, ErrorManager.WRITE_FAILURE);
            }
        }
    }
    
    protected String getCurrentTimeForFileName() {
        Timestamp tm = new Timestamp(System.currentTimeMillis());
        SimpleDateFormat dt = new SimpleDateFormat("yyyy-MM-dd_HHmmssSSS");
        return dt.format(tm);
    }
    
    /**
     * 期限切れのログファイルを除去する.<br>
     * @param expire ログファイル
     */
    public static void purgeLogs() {
        // 期限切れまでの日数の設定を取得する.
        String strExpires = LogManager.getLogManager().getProperty(".expires");
        int expires = 0;
        if (strExpires != null && strExpires.trim().length() > 0) {
            try {
                expires = Integer.valueOf(strExpires);
            } catch (Exception ex) {
                expires = 0;
            }
        }
        if (expires <= 0) {
            expires = DEFAULT_EXPIRES;
        }
        
        // 期限切れになった日時の算定
        final long expired = System.currentTimeMillis() - (expires * 24 * 60 * 1000);
        
        // ログディレクトリ上のログファイルをすべて走査して期限切れであれば削除する.
        File logDir = getLogDir();
        for (File log : logDir.listFiles()) {
            if (!log.isFile()) {
                // ファイルでない
                continue;
            }
            String name = log.getName();
            if ( !name.endsWith(LOGFILE_EXT)) {
                // 拡張子が一致しない
                continue;
            }
            
            long lastModified = log.lastModified();
            if (lastModified <= 0) {
                // 時刻が取得できない
                continue;
            }
            if (lastModified < expired) {
                // 期限切れ
                log.delete();
            }
        }
    }

}
