/*
 * 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;


/**
 * ニーモニック・ユーティリティーです。
 * <p>
 * @author cypher256
 */
public class Mnemonics {

	/** ニーモニック文字の正規表現ブロック */
	private static final String MNEMONIC_CHARS = "[\\w\\.@]";

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

	/**
	 * 指定した値に含まれる (&A) のような日本語ニーモニックを除去します。
	 * ニーモニック対象文字と中括弧も含めて除去されます。
	 * <p>
	 * @param ja 値
	 * @return ニーモニック除去後の値
	 */
	public static String removeJaMnemonic(String ja) {

		if (hasJaMnemonic(ja)) {

			// 日本語向けニーモニックの除去
			ja = ja.replaceFirst("\\(\\&" + Mnemonics.MNEMONIC_CHARS + "\\)", "");

			// 日本語向けニーモニックのバグを除去 (&)
			ja = ja.replaceFirst("\\(\\&\\)", "");
		}
		return ja;
	}

	/**
	 * 英語リソース文字列からニーモニック制御文字 & を除去します。
	 * ニーモニック対象文字自体は除去されません。
	 * <p>
	 * @param en 英語リソース文字列
	 * @return ニーモニック制御文字除去後の文字列
	 */
	public static String removeEnMnemonic(String en) {

		if (hasEnMnemonic(en)) {
			return en.replaceFirst("\\&(" + Mnemonics.MNEMONIC_CHARS + ")", "$1");
		}
		return en;
	}

	/**
	 * (&A) のような日本用のニーモニックが既に含まれているか判定します。
	 * <p>
	 * @param value リソース文字列
	 * @return 含まれている場合は true
	 */
	public static boolean hasJaMnemonic(String value) {
		return
			value.contains("(&") && // 高速化のため先に contains で絞り込み
			value.matches("(?s)^.*?\\(\\&" + Mnemonics.MNEMONIC_CHARS + "\\).*$");
	}

	/**
	 * 英語ニーモニックが含まれているか判定します。
	 * &lt; などはニーモニックではないので false を返します。
	 * <p>
	 * @param value 値
	 * @return 含まれている場合は true
	 */
	public static boolean hasEnMnemonic(String value) {

		// &nbsp; などがある場合、ニーモニックが含まれないことを前提とした判定。
		// これ以外の & は日本語文字列に & が含まれるかを辞書で判断。
		return
			value.contains("&") &&
			!value.contains("& ") && // & の後に半角空白
			!value.contains("&&&") &&
			!containsHtmlReference(value) &&
			!containsUrlParameters(value);
	}

	/**
	 * html 実体参照文字が含まれているか判定します。
	 * <p>
	 * @param value 値
	 * @return 含まれている場合は true
	 */
	public static boolean containsHtmlReference(String value) {

		return
			value.contains("&lt;") ||
			value.contains("&gt;") ||
			value.contains("&amp;") ||
			value.contains("&quot;") ||
			value.contains("&yen;") ||
			value.contains("&nbsp;");
	}

	/**
	 * & を含むパラメーター付きの URL か判定します。
	 * <p>
	 * @param value 値
	 * @return & を含むパラメーター付きの URL の場合は true
	 */
	public static boolean containsUrlParameters(String value) {

		return
			!value.contains("? ") && // 除外 例）&Select (? = any character
			!value.contains("?=") && // 除外 例）(?=any character
			value.contains("?") &&
			value.contains("=") &&
			value.contains("&");
	}

	/**
	 * ニーモニックを英語用から日本用に変換します。<br>
	 * 例）&x → (&X)
	 * <p>
	 * @param enWithMnemonic	英語リソース文字列  （ニーモニック有り）
	 * @param en				英語リソース文字列  （ニーモニック無し）
	 * @param ja				日本語リソース文字列（ニーモニック無し）
	 * @return					日本語リソース文字列（ニーモニック有り）
	 */
	public static String convertMnemonicEnToJa(String enWithMnemonic, String en, String ja) {

		if (enWithMnemonic.length() == en.length()) {
			return ja;
		}

		// 日本語リソースにニーモニック "&" が含まれる場合は英語側もニーモニックではないと判断。
		// "&&" はニーモニックではなく、"&" そのものを示すため除外。
		// そのまま処理してしまうと "K&R" が "K&R(&R)" のようになる。
		// translation.properties 上は英語リソース文字列に & はない。例："KR"
		if (ja.contains("&") && !ja.contains("&&")) {
			return ja;
		}

		String mnemonicChar = enWithMnemonic.replaceFirst(
				"(?s)^.*?\\&(" + Mnemonics.MNEMONIC_CHARS + ").*$",
				"$1");

		if (mnemonicChar.length() != 1) {
			StringBuilder sb = new StringBuilder();
			sb.append("Mnemonic invalid length:" + mnemonicChar.length());
			sb.append(", enValue:" + enWithMnemonic);
			sb.append(", enValueNonMnemonic:" + en);
			sb.append(", mnemonicChar:" + mnemonicChar);
			throw new IllegalArgumentException(sb.toString());
		}
		String mnemonicJa = "(&" + mnemonicChar.toUpperCase() + ")";

		// 例）
		// hoge @Ctrl+X		-> hoge (&H)@Ctrl+X
		// '@Override'		-> '@Override'(&O)

		String jaWithMnemonic = ja.replaceFirst("(?s)^('.+?|.+?)(" +
			"(\\s*\\.{3,4}|)@\\p{ASCII}+|" +	// 例）末尾が @Ctrl+Shift+X
			"\\.{3,4}(</a>|)\\s*|" +			// 例）末尾が ... or ....
			"\\s*|" +							// 例）末尾が 空白
			":\\s*|" +							// 例）末尾が :空白
			":\\s+\\{[0-9]\\}\\s*|" +			// 例）末尾が :{数字}
			"(:|)\\s+\\(.+\\)\\s*|" +			// 例）末尾が :(文字列)
			"\\s+(-*|)>+\\s*" +					// 例）末尾が > or >> or -->
			")$", "$1" + mnemonicJa + "$2");

		return jaWithMnemonic;
	}
}
