/*
 * Copyright (C) 2014 kgto.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301  USA
 */
/*
 * $Id: DebugProcess.java 82 2014-09-22 00:48:50Z tuna_p $
 */

package WebScraping;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.logging.FileHandler;
import java.util.logging.Formatter;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogManager;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
import javax.swing.text.MutableAttributeSet;
import javax.swing.text.html.HTML;

/**
 * デバック情報.
 * カレントディレクトリに設定ファイル(Debug.prop)を置くことで、デバックログの出力を制御する。
 * @author kgto
 */
public class DebugProcess {
    // 設定ファイル名
    protected static final String configurationFilename = "Debug.prop";
    // ロガー名
    protected static final Logger logger = Logger.getLogger("WebScraping");
    // ログ出力デフォルトレベル
    protected static final Level loggerlevel = Level.FINEST;
    
    
    /**
     * ログ出力設定.
     * ログ設定ファイルの存在をチェック、(最終的な)ログレベルにより、
     * ファイルハンドラの設定と出力書式の設定を行う。
     */
    public static void debuglog_set() {
        try {
            initLogConfiguration();

            if(Level.ALL.equals(logger.getLevel())) {
                //logger.addHandler(new FileHandler("WebScraping%g.log", 100000, 2));
                logger.addHandler(new FileHandler("WebScraping%g.log", true));
            }
            setFomatter();
            
        } catch (IOException | SecurityException ex) {
            Logger.getLogger(DebugProcess.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    
    /**
     * ログ出力設定解除.
     */
    public static void debuglog_unset() {
    }
    
    
    /**
     * デバック出力(ＨＴＭＬ解析-タグ＆属性).
     * ＨＴＭＬのタグと属性の解析状態を出力する。
     * 書式: 9 : x : タグ名 [属性名]属性数 = 属性値<br>
     * 凡例: 9 = 階層レベル(count値), x = F(tagの開始)/E(tagの終了)/S(単独tag)の何れか１文字<br>
     * @param tag タグ
     * @param attr 属性
     * @param methodname このメソッドを呼び出した親メソッド名
     * @param count ＨＴＭＬタグの階層レベル
     */
    public static void htmlinfo(HTML.Tag tag, MutableAttributeSet attr, 
            String methodname, int count) {
        
        // ログ出力レベルチェック
        if(logger.getLevel() == null) {
            return;
        }
        if(logger.getLevel().intValue() > loggerlevel.intValue()) {
            return;
        }
        
        // 編集処理
        char kbn = ' ';
        if("handleStartTag".equals(methodname)) {
            kbn = 'F';
        }
        if("handleEndTag".equals(methodname)) {
            kbn = 'E';
        }
        if("handleSimpleTag".equals(methodname)) {
            kbn = 'S';
        }
        
        StringBuilder strBuf = new StringBuilder(80);
        strBuf.append(count).append(" : ");
        strBuf.append(kbn).append(" : ");
        strBuf.append(tag.toString());
        // 属性情報
        if(attr != null) {
            if(attr.getAttributeCount() != 0) {
                AttributeData handleAttrData = new AttributeData();
                handleAttrData.add(tag, attr);
                for(int i = 0; i < handleAttrData.size; i++) {
                    strBuf.append(" [");
                    strBuf.append(handleAttrData.getattrname(i));
                    strBuf.append("]");
                    strBuf.append(handleAttrData.getcount(i));
                    strBuf.append(" = ");
                    strBuf.append(handleAttrData.getattrvalue(i));
                }
            }
        }
        
        logger.log(loggerlevel, strBuf.toString());
    }
    
    /**
     * デバック出力(メッセージ).
     * 引数に渡された任意のメッセージを出力する。
     * @param str メッセージ
     * @param methodname このメソッドを呼び出した親メソッド名
     */
    public static void htmlinfo(String str, String methodname) {
        logger.log(loggerlevel, str);
    }
    
    public static void htmlinfo(String str) {
        logger.log(loggerlevel, str);
    }

    /**
     * デバック出力(ＨＴＭＬ解析-本文).
     * 本文の内容を出力する。
     * @param data 本文(ＨＴＭＬ内の文字列)
     * @param methodname このメソッドを呼び出した親メソッド名
     */
    public static void htmlinfo(char[] data, String methodname) {
        String dat = new String(data);
        logger.log(loggerlevel, dat);
    }
    
    public static void htmlinfo(char[] data) {
        String dat = new String(data);
        logger.log(loggerlevel, dat);
    }
    
    /**
     * デバック出力(検索キー).
     * 検索キー(SearchData)の内容を出力する。
     * @param skey 
     */
    public static void searchDatainfo(SearchData skey) {
        
        StringBuilder strBuf = new StringBuilder(30);
        strBuf.append("SearchData KEY tag[");
        strBuf.append(skey.getHtmltag());
        strBuf.append("] ID[");
        strBuf.append(skey.getHtmlid());
        strBuf.append("] CLASS[");
        strBuf.append(skey.getHtmlclass());
        strBuf.append("]\n");
        
        logger.log(loggerlevel, strBuf.toString());
    }
    
    /**
     * ログ出力設定ファイルチェック.
     * 設定ファイルの存在をチェックし存在する場合、設定ファイルの内容を設定する。
     */
    private static void initLogConfiguration() {
        
        File file = new File(configurationFilename);
        try {
            if(file.exists()) {
                FileInputStream inputStream = new FileInputStream(file);
                // 設定ファイルの読み込み
                LogManager.getLogManager().readConfiguration(inputStream);
            }
            
        } catch (FileNotFoundException ex) {
            Logger.getLogger(DebugProcess.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IOException ex) {
            Logger.getLogger(DebugProcess.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    
    /**
     * ログ出力フォーマッター設定.
     * ファイルへログ出力時の書式を設定する。
     */
    private static void setFomatter() {
        Handler[] handlers = logger.getHandlers();
        for(int i = 0 ; i < handlers.length ; i++) {
            if(handlers[i] instanceof java.util.logging.FileHandler) {
                handlers[i].setFormatter(new HtmlFormatter());
            }
        }
    }
    
}

/**
 * ログ出力フォーマッター.
 * @author kgto
 */
class HtmlFormatter extends Formatter {
    /**
     * Logの出力文字列を生成する。
     * 出力書式：<br>
     * YYYY-MM-DD HH:SS:MM ログレベル<メソッド名>メッセージ
     */    
    @Override
    public synchronized String format(final LogRecord aRecord) {
        
        final StringBuffer message = new StringBuffer(100);

        long millis = aRecord.getMillis();
        String time = String.format("%tF %<tT", millis);
        
        message.append(time);
        message.append(' ');
        
        message.append(aRecord.getLevel());
        message.append('<');
        String methodName = aRecord.getSourceMethodName();
        message.append(methodName != null ? methodName : "N/A");
        message.append('>');
        
        message.append(formatMessage(aRecord));
        message.append('\n');
        
        // 例外エラーの場合、エラー内容とスタックトレース出力
        Throwable throwable = aRecord.getThrown();
        if (throwable != null) {
            message.append(throwable.toString());
            message.append('\n');
            for (StackTraceElement trace : throwable.getStackTrace()) {
                message.append('\t');
                message.append(trace.toString());
                message.append('\n');
            }
        }
        return message.toString();
    }
}
