/*
 * 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.org.apache.commons.io.IOUtils;
import jp.sourceforge.mergedoc.org.apache.commons.lang.CharEncoding;
import jp.sourceforge.mergedoc.pleiades.Pleiades;
import jp.sourceforge.mergedoc.pleiades.log.Logger;

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

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

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

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

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

	/** 訳無しロガーインスタンスの初期化 */
	static {
		if (Pleiades.getPleiadesOption().enabledNotFoundLog) {
			notFoundLog = new TranslationNotFoundProperties();
			notFoundLog.init();
		} else {
			notFoundLog = new TranslationNotFoundProperties() {
				@Override
				public void put(TranslationString enTs) {
				}
			};
		}
	}

	/** 訳無しロガー出力から除外する要素のセット */
	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 = PatternCache.get(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, CharEncoding.UTF_8);

			// 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, "訳無しログライターを生成できませんでした。");
		}
	}

	/**
	 * 翻訳できなかった英語を追加します。
	 * <ul>
	 * <li>引数は trim などで操作されるため、状態が変化します。
	 * <li>パフォーマンスを考慮し、非 debug 時、このメソッドは空実装になります。
	 * </ul>
	 * @param enTs 英語文字列
	 */
	public void put(TranslationString enTs) {

		// trimForce された状態を元に戻す
		enTs.init();

		println(enTs.toString().trim());
		println(enTs.trim());
		println(enTs.trimForce());
	}

	/**
	 * ファイルへ出力します。
	 * <p>
	 * @param en ニーモニックを含まない英語リソース文字列
	 */
	protected 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);
		}
	}
}
