/*
 * Copyright (c) 2005- Shinji Kashihara.
 * All rights reserved. This program are made available under
 * the terms of the Eclipse Public License v1.0 which accompanies
 * this distribution, and is available at epl-v10.html.
 */
package jp.sourceforge.mergedoc.pleiades.resource;

import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintStream;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.regex.Pattern;

import jp.sourceforge.mergedoc.pleiades.Pleiades;
import jp.sourceforge.mergedoc.pleiades.log.Logger;

import org.apache.commons.io.IOUtils;

/**
 * 翻訳プロパティーに要素が見つからなかった場合の情報を出力するためのプロパティーです。
 * <p>
 * @author cypher256
 */
public class TranslationNotFoundProperties {

	/** ロガー */
	private static final Logger log = Logger.getLogger(TranslationNotFoundProperties.class);

	/** このログが有効なことを示すシステム・プロパティーのキー */
	public static final String NOT_FOUND_PROPERTIES_ENABELD = "pleiades.enabled.not.found.log";

	/** 訳無しログ出力ファイル名 */
	private static final File NOT_FOUND_LOG_FILE_NAME =
		Pleiades.getResourceFile("translation-notfound.properties");

	/** 訳無しログ出力から除外する要素をリストしたファイル名 */
	private static final File EXCLUDE_LIST_FILE =
		Files.getFile("translation-notfound-exclud.list");

	/** 訳無しロガーインスタンス */
	private static TranslationNotFoundProperties notFoundLog;

	/** 訳無しロガーインスタンスの初期化 */
	static {
		String enabled = System.getProperty(NOT_FOUND_PROPERTIES_ENABELD);
		if (Boolean.valueOf(enabled)) {
			notFoundLog = new TranslationNotFoundProperties();
			notFoundLog.init();
		} else {
			notFoundLog = new TranslationNotFoundProperties() {
				@Override
				public void println(String enValueNonMnemonic) {
				}
			};
		}
	}

	/** 訳無しロガー出力から除外する要素のセット */
	private Set<String> excludSet;

	/** 訳無しロガー出力から除外する要素のセット（正規表現） */
	private Set<Pattern> excludPatternSet;

	/** 訳無しロガーに出力済みの要素（複数出力抑止用） */
	private Set<String> loggedSet;

	/** ファイル PrintStream */
	private PrintStream out;

	/**
	 * インスタンス化できません。
	 */
	private TranslationNotFoundProperties() {
	}

	/**
	 * 訳無しロガーのインスタンスを取得します。
	 * <p>
	 * @return 訳無しロガーのインスタンス
	 */
	public static TranslationNotFoundProperties getInstance() {
		return notFoundLog;
	}

	/**
	 * 初期化します。
	 */
	private void init() {

		excludSet = new HashSet<String>();
		excludPatternSet = new HashSet<Pattern>();
		loggedSet = Collections.synchronizedSet(new HashSet<String>());

		BufferedReader in = null;
		try {
			in = new BufferedReader(new FileReader(EXCLUDE_LIST_FILE));

			for (String line; (line = in.readLine()) != null;) {
				if (!line.startsWith("#")) {
					String str = line.replaceAll("\\\\n", "\n");
					String prefix = "%REGEX%";
					if (str.startsWith(prefix)) {
						Pattern pattern = Pattern.compile(str.replaceFirst(prefix, ""));
						excludPatternSet.add(pattern);
					} else {
						excludSet.add(str);
					}
				}
			}

		} catch (IOException e) {
			String msg = "訳無しログ出力除外リストをロードできませんでした。";
			log.fatal(msg);
			throw new IllegalStateException(msg, e);

		} finally {
			IOUtils.closeQuietly(in);
		}

		try {
			out = new PrintStream(
					new BufferedOutputStream(new FileOutputStream(NOT_FOUND_LOG_FILE_NAME)),
					true);

			// VM シャットダウン・フックとしてファイル PrintStream のクローズを登録
			Runtime.getRuntime().addShutdownHook(new Thread() {
				@Override
				public void run() {
					PrintStream out = TranslationNotFoundProperties.getInstance().out;
					IOUtils.closeQuietly(out);
				}
			});

		} catch (IOException e) {
			log.fatal(e, "訳無しログライターを生成できませんでした。");
		}
	}

	/**
	 * 指定された要素をログ出力します。
	 * <p>
	 * @param en ニーモニックを含まない英語リソース文字列
	 */
	public void println(String en) {

		if (loggedSet.contains(en)) {
			return;
		}
		if (excludSet.contains(en)) {
			return;
		}
		for (Pattern pattern : excludPatternSet) {
			if (pattern.matcher(en).matches()) {
				return;
			}
		}
		loggedSet.add(en);

		String key = Property.escapeKey(en);
		String value = Property.escapeValue(en);

		// 翻訳しやすいように右辺の改行文字は空白に置換
		value = value.replace("\\r", " ");
		value = value.replace("\\n", " ");

		if (key.matches("(?s).*\\p{Alpha}{2}.*")) {
			out.println(key + "=" + value);
		}
	}
}
