/*
 * 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.aspect.resource;

import java.util.Arrays;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import jp.sourceforge.mergedoc.pleiades.aspect.advice.JointPoint;
import jp.sourceforge.mergedoc.pleiades.log.Logger;
import jp.sourceforge.mergedoc.pleiades.resource.Mnemonics;

/**
 * 翻訳ユーティリティーです。
 * <p>
 * このクラスのメソッドは、バイトコード変換されたクラスから
 * 呼び出しやすいように、static メソッドで構成されています。
 * <p>
 * @author cypher256
 */
public class Translations {

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

	/** 翻訳辞書 */
	private static final DynamicTranslationDictionary dictionary = DynamicTranslationDictionary.getInstance();

	/** コンボの値を保持する退避マップ */
	private static final Map<String, String> backupMap = new ConcurrentHashMap<String, String>();

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

	/**
	 * 指定された英語リソース文字列を翻訳します。
	 * <p>
	 * @param en 英語リソース文字列
	 * @param jointPoint ジョイント・ポイント
	 * @return 翻訳後文字列。翻訳できない場合は引数と同じ。
	 */
	public static String translate(String en, JointPoint jointPoint) {

		if (en == null)
			return null;
		return dictionary.lookup(en, jointPoint);
	}

	/**
	 * 指定された英語リソース文字列の配列を翻訳します。
	 * <p>
	 * @param ens 英語リソース文字列の配列
	 * @param jointPoint ジョイント・ポイント
	 * @return 翻訳後文字列。翻訳できない場合は引数と同じ。
	 */
	public static String[] translate(String[] ens, JointPoint jointPoint) {

		if (ens == null)
			return null;
		for (int i = 0; i < ens.length; i++) {
			ens[i] = translate(ens[i], jointPoint);
		}
		return ens;
	}

	/**
	 * 指定された英語リソース・オブジェクトを翻訳します。
	 * <p>
	 * @param en 英語リソース・オブジェクト
	 * @param jointPoint ジョイント・ポイント
	 * @return 翻訳後文字列。翻訳できない場合は引数と同じ。
	 */
	public static Object translate(Object en, JointPoint jointPoint) {

		if (en == null) {
			return null;
		} else if (en instanceof String) {
			return translate((String) en, jointPoint);
		} else if (en instanceof String[]) {
			return translate((String[]) en, jointPoint);
		} else if (en instanceof Object[]) {
			return translate((Object[]) en, jointPoint);
		}
		return en;
	}

	/**
	 * 指定された英語リソース・オブジェクトを翻訳します。
	 * <p>
	 * @param enObjs 英語リソース・オブジェクト
	 * @param jointPoint ジョイント・ポイント
	 * @return 翻訳後文字列。翻訳できない場合は引数と同じ。
	 */
	public static Object[] translate(Object[] enObjs, JointPoint jointPoint) {

		if (enObjs instanceof String[]) {
			return translate((String[]) enObjs, jointPoint);
		}
		return enObjs;
	}

	/**
	 * 指定された英語リソース文字列をメニュー用に翻訳します。
	 * <p>
	 * @param en 英語リソース文字列
	 * @param jointPoint ジョイント・ポイント
	 * @return 翻訳後文字列。翻訳できない場合は引数と同じ。
	 */
	public static String translateMenu(String en, JointPoint jointPoint) {

		if (en == null)
			return null;
		String[] splitsAccelerator = en.split("\t");
		String enMenuText = splitsAccelerator[0];

		// 現在このメソッド呼び出しは限定されているため、先頭 & 除去は不要
		/*
		if (!enMenuText.matches("\\p{ASCII}+")) {
			// 日本語メニューの新規項目先頭に & が付加されてしまうのを回避
			enMenuText = enMenuText.replaceFirst("^&", "");
		}
		*/
		if (splitsAccelerator.length == 1) {
			return dictionary.lookup(enMenuText, jointPoint);
		} else {
			return dictionary.lookup(enMenuText, jointPoint) + "\t" + splitsAccelerator[1];
		}
	}

	/**
	 * 指定された英語リソース文字列を翻訳します。
	 * ニーモニックは処理されません。
	 * <p>
	 * @param en 英語リソース文字列
	 * @param jointPoint ジョイント・ポイント
	 * @return 翻訳後文字列。翻訳できない場合は引数と同じ。
	 */
	public static String translateIgnoreMnemonic(String en, JointPoint jointPoint) {

		if (en == null)
			return null;
		return dictionary.lookupIgnoreMnemonic(en, jointPoint);
	}

	/**
	 * 指定された英語リソース文字列をバックアップし、翻訳します。
	 * <p>
	 * @param en 英語リソース文字列
	 * @param jointPoint ジョイント・ポイント
	 * @return 翻訳後文字列。翻訳できない場合は引数と同じ。
	 */
	public static String translateBackup(String en, JointPoint jointPoint) {

		if (en == null)
			return null;
		String ja = dictionary.lookup(en, jointPoint);
		backupMap.put(ja, en);
		return ja;
	}

	/**
	 * 指定された英語リソース文字列をバックアップし、翻訳します。
	 * ニーモニックは処理されません。
	 * <p>
	 * @param en 英語リソース文字列
	 * @param jointPoint ジョイント・ポイント
	 * @return 翻訳後文字列。翻訳できない場合は引数と同じ。
	 */
	public static String translateBackupIgnoreMnemonic(String en, JointPoint jointPoint) {

		if (en == null)
			return null;
		String ja = dictionary.lookupIgnoreMnemonic(en, jointPoint);
		backupMap.put(ja, en);
		return ja;
	}

	/**
	 * 指定された英語リソース文字列配列をバックアップし、翻訳します。
	 * ニーモニックは処理されません。
	 * <p>
	 * @param ens 英語リソース文字列
	 * @param jointPoint ジョイント・ポイント
	 * @return 翻訳後文字列。翻訳できない場合は引数と同じ。
	 */
	public static String[] translateBackupIgnoreMnemonic(String[] ens, JointPoint jointPoint) {

		if (ens == null)
			return null;
		for (int i = 0; i < ens.length; i++) {
			ens[i] = translateBackupIgnoreMnemonic(ens[i], jointPoint);
		}
		return ens;
	}

	/**
	 * translateBackup で翻訳された日本語リソース文字列を英語に戻します。
	 * <p>
	 * @param ja 日本語リソース文字列
	 * @param jointPoint ジョイント・ポイント
	 * @return 英語文字列。戻せない場合は引数と同じ。
	 */
	public static String restore(String ja, JointPoint jointPoint) {

		if (ja == null)
			return null;
		String en = backupMap.get(ja);
		if (en == null) {
			return ja;
		}
		return en;
	}

	/**
	 * 指定された英語リソース文字列を翻訳します。
	 * 空白が 1 つ以上含まれる場合のみ翻訳されます。
	 * <p>
	 * @param en 英語リソース文字列
	 * @param jointPoint ジョイント・ポイント
	 * @return 翻訳後文字列。翻訳できない場合は引数と同じ。
	 */
	public static String translateSentence(String en, JointPoint jointPoint) {

		if (en != null && en.trim().contains(" ")) {
			return dictionary.lookup(en, jointPoint);
		}
		return en;
	}

	/**
	 * 指定された日本用ニーモニックを含むリソース文字列を翻訳します。
	 * 引数に含まれる日本用ニーモニックは翻訳前に除去されます。
	 * <p>
	 * @param en 日本用ニーモニックが含まれたリソース文字列
	 * @param jointPoint ジョイント・ポイント
	 * @return 翻訳後文字列。翻訳できない場合は引数と同じ。
	 */
	public static String translateWithoutJaMnemonic(String en, JointPoint jointPoint) {

		if (en == null)
			return null;
		en = Mnemonics.removeJaMnemonic(en);
		return dictionary.lookupIgnoreMnemonic(en, jointPoint);
	}

	// 不完全なため廃止。2009.09.10
	/**
	 * 指定された英語ヘルプ入力ストリームを翻訳します。
	 * <p>
	 * @param enIs 英語ヘルプ入力ストリーム
	 * @param jointPoint ジョイント・ポイント
	 * @return 翻訳後入力ストリーム。翻訳できない場合は引数と同じ。
	public static InputStream translateHelp(InputStream enIs, JointPoint jointPoint) {

		// 今のところ、debug 時のみ有効
		// -> ヘルプ検索ができるようになれば公開予定。現在は英語でしか検索不可。
		if (!log.isDebugEnabled())
			return enIs;

		if (enIs == null)
			return null;

		HelpHtmlParser parser = new HelpHtmlParser(enIs);
		for (HtmlFragment f : parser) {
			String en = f.getText();
			String ja = dictionary.lookupHelp(en, jointPoint);
			f.setText(ja);
		}
		return parser.getInputStream();
	}
	 */

	/**
	 * 指定した値をデバッグ・ログとして出力します。
	 * <p>
	 * @param obj オブジェクト
	 */
	public static void debug(Object obj) {

		StringBuilder sb = new StringBuilder();
		sb.append("Translations#debug ");
		if (obj != null) {
			sb.append(obj.getClass().getName());
		}
		sb.append("[");
		sb.append(obj);
		sb.append("]");
		log.info(sb.toString());
	}

	/**
	 * 指定した値をデバッグ・ログとして出力します。
	 * <p>
	 * @param en 英語リソース・オブジェクト
	 * @param jointPoint ジョイント・ポイント
	 */
	public static void debug(Object en, JointPoint jointPoint) {

		StringBuilder sb = new StringBuilder();
		sb.append("Translations#debug ");
		if (jointPoint != null) {
			sb.append("(");
			sb.append(jointPoint.getClassName());
			sb.append("#");
			sb.append(jointPoint.getMethodName());
			sb.append(") ");
		}
		if (en != null) {
			sb.append(en.getClass().getName());
		}
		sb.append("[");
		sb.append(en);
		sb.append("] -> [");
		sb.append(translate(en, jointPoint));
		sb.append("]");
		log.info(sb.toString());
	}

	/**
	 * 指定した値をデバッグ・ログとして出力します。
	 * <p>
	 * @param en 英語リソース・オブジェクト
	 * @param jointPoint ジョイント・ポイント
	 * @param key キー
	 */
	public static void debug(Object en, JointPoint jointPoint, String key) {

		StringBuilder sb = new StringBuilder();
		sb.append("Translations#debug ");
		if (jointPoint != null) {
			sb.append("(");
			sb.append(jointPoint.getClassName());
			sb.append("#");
			sb.append(jointPoint.getMethodName());
			sb.append(") ");
		}
		if (en != null) {
			sb.append(en.getClass().getName());
		}
		sb.append("[");
		sb.append(en);
		sb.append("] -> [");
		sb.append(translate(en, jointPoint));
		sb.append("]");

		sb.append(" key=");
		sb.append(key);
		log.info(sb.toString());
	}

	/**
	 * 指定した値をデバッグ・ログとして出力します。
	 * <p>
	 * @param ens 英語リソース・オブジェクトの配列
	 * @param jointPoint ジョイント・ポイント
	 */
	public static void debug(String[] ens, JointPoint jointPoint) {

		StringBuilder sb = new StringBuilder();
		sb.append("Translations#debug ");
		if (jointPoint != null) {
			sb.append("(");
			sb.append(jointPoint.getClassName());
			sb.append("#");
			sb.append(jointPoint.getMethodName());
			sb.append(") ");
		}
		if (ens != null) {
			sb.append(ens.getClass().getName());
		}
		sb.append(Arrays.asList(ens));
		sb.append(" -> ");
		sb.append(Arrays.asList(translate(ens, jointPoint)));
		log.info(sb.toString());
	}

	/**
	 * 現在のスレッドのスタックトレースをログに出力します。
	 * <p>
	 * @param obj 英語リソース・オブジェクト
	 * @param jointPoint ジョイント・ポイント
	 */
	public static void printStackTrace(Object obj, JointPoint jointPoint) {
		log.info(new Exception(), "Translator#debug " + (obj == null ? "" : obj.getClass().getName()) + "[" + obj
				+ "] -> [" + translate(obj, jointPoint) + "]");
	}
}
