package ash.reverse.parser;

/**
 * 可視性以外のJavaの修飾子の定義
 * <pre>
 * 使用例：
 * int flags |= Mod.mask("static");
 * flags |= Mod.mask("final");
 * if(Mod.STATIC.in(flags)) {}
 * Mod mod = Mod.value(token);
 * if(mod.in(flags)) {...}
 * </pre>
 */
public enum Mod {
	ABSTRACT("abstract"),
	STATIC("static"),
	FINAL("final"),
	NATIVE("native"),
	STRICTFP("strictfp"),
	SYNCHRONIZED("synchronized"),
	TRANSIENT("transient"),
	VOLATILE("volatile"),
	UNDEF("????");

	Mod(String keyword) { this.keyword = keyword; }

	/**
	 * テストコード
	 */
	public static void main(String[] args) {
		print("native -> " + Mod.value("native"));
		print("whatisthis -> " + Mod.value("whatisthis"));
		print("#native -> " + Mod.index("native"));
		print("#whatisthis -> " + Mod.index("whatisthis"));
		for(Mod mod : Mod.values())
			print(mod.name() + ": " + mod.keyword +
				  ", index = " + mod.index() +
				  ", mask = " + mod.mask()
				  );
		int modifiers = 0;
		modifiers |= Mod.mask("static");
		modifiers |= Mod.mask("final");
		modifiers |= Mod.mask("synchronized");
		print("modifiers=" + modifiers);
		modifiers |= Mod.mask("whatisthis");
		print("modifiers=" + modifiers);
		print("'" + Mod.toCode(modifiers) + "'");

		assert Mod.STATIC.in(modifiers): "Mod.STATIC.in(modifiers)";
		assert !Mod.UNDEF.in(modifiers): "Mod.UNDEF.in(modifiers)";
		assert !Mod.TRANSIENT.in(modifiers): "Mod.TRANSIENT.in(modifiers)";
	}

	private static void print(Object obj) { System.out.println(obj); }

	/** Javaキーワード表現 */
	public final String keyword;
	public String keyword() { return keyword; }

	/**
	 * 0から始まる一意な連番
	 * 修飾値をint型のビットとして表す時のビット位置を示すインデックス
	 */
	private int index;
	public int index() { return index; }

	/** indexの開始値 */
	private static int seq = 0;
	static {
		for(Mod m : values()) m.index = seq++;
	}

	/**
	 * index値のビットに対応する整数値を返す。
	 * @return index値のビットに対応する整数値
	 */
	public int mask() {
		return 1 << index;
	}

	/**
	 * 指定された修飾値セットにこの列挙値が含まれているか判定する。
	 * @param modifiers 修飾値セット
	 * @return この列挙値が修飾値セットに含まれているならばtrue
	 */
	public boolean in(int modifiers) {
		return this != UNDEF && (modifiers & (1 << index)) != 0;
	}
		
	/**
	 * Javaキーワードからenum値を取得する。
	 * @param keyword Javaキーワード
	 * @return 対応するenum値
	 */
	public static Mod value(String keyword) {
		for(Mod mod : values()) {
			if(mod.keyword.equals(keyword)) return mod;
		}
		return UNDEF;
	}

	/**
	 * Javaキーワードからindex値を取得する。
	 * @param keyword Javaキーワード
	 * @return 対応するindex値（存在しない場合は-1）
	 */
	static int index(String keyword) {
		Mod mod = value(keyword);
		if(mod == UNDEF) return -1;
		return mod.index;
	}

	/**
	 * Javaキーワードからindex値のビットに対応する整数値を取得する。
	 * @param keyword Javaキーワード
	 * @return 対応するマスク値（存在しない場合は0）
	 */
	public static int mask(String keyword) {
		int index = index(keyword);
		if(index < 0) return 0;
		return 1 << index;
	}

	/**
	 * 指定された修飾値セットをJavaキーワードで表した文字列を返す。
	 * @param modifiers 修飾値セット
	 * @return 指定された修飾値セットのJavaキーワード表現
	 */
	public static String toCode(int modifiers) {
		StringBuilder sb = new StringBuilder();
		for(Mod mod : values()) {
			if((modifiers & mod.mask()) != 0)
				sb.append(mod.keyword + " ");
		}
		return sb.toString();
	}
}
