package net.t7seven7t.craftfx.util;
import com.google.common.collect.Lists;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.chat.ClickEvent;
import net.md_5.bungee.api.chat.HoverEvent;
import net.md_5.bungee.api.chat.TextComponent;
import net.t7seven7t.craftfx.CraftFX;
import net.t7seven7t.util.EnumUtil;
import org.bukkit.command.CommandSender;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.entity.Player;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static net.md_5.bungee.api.ChatColor.BOLD;
import static net.md_5.bungee.api.ChatColor.ITALIC;
import static net.md_5.bungee.api.ChatColor.MAGIC;
import static net.md_5.bungee.api.ChatColor.RESET;
import static net.md_5.bungee.api.ChatColor.STRIKETHROUGH;
import static net.md_5.bungee.api.ChatColor.UNDERLINE;
import static net.md_5.bungee.api.ChatColor.getByChar;
import static net.md_5.bungee.api.ChatColor.translateAlternateColorCodes;
/**
* Utility for sending and formatting of messages
*/
public class MessageUtil {
private static final Pattern ACTION_SELECTOR_PATTERN = Pattern
.compile("(?:\\[([^\\[\\]]+)\\])");
private static final Pattern SPLIT_ACTION_PATTERN = Pattern.compile("([^|]+)");
private static final Pattern SPLIT_FORMAT_PATTERN = Pattern.compile("([^&\\u00A7]+)");
private static final Pattern REPLACER_PATTERN = Pattern.compile("([^%]*)%([\\w]+)%");
public static void message(CommandSender sender, String message, Object... args) {
ConfigurationSection messages = CraftFX.instance().getMessages(Locale.ENGLISH);
if (messages.contains(message)) {
String key = message;
message = messages.getString(key);
if (!message.equalsIgnoreCase(key)) {
message(sender, message, args);
return;
}
}
message = replace(message, sender);
if (sender instanceof Player) {
((Player) sender).spigot().sendMessage(translateText(String.format(message, args)));
} else {
sender.sendMessage(color(String.format(message, args)));
}
}
public static String color(String message) {
return translateAlternateColorCodes('&', message);
}
public static BaseComponent translateText(String message) {
List<BaseComponent[]> hoverText = null;
List<BaseComponent[]> clickText = null;
Matcher m = ACTION_SELECTOR_PATTERN.matcher(message);
while (m.find()) {
Matcher m1 = SPLIT_ACTION_PATTERN.matcher(m.group(1));
List<String> split = Lists.newArrayList();
while (m1.find()) split.add(m1.group(1));
if (split.size() != 4) continue;
if (split.get(0).equalsIgnoreCase("hover")) {
HoverEvent.Action action = EnumUtil.matchEnumValue(HoverEvent.Action.class,
split.get(1));
BaseComponent[] components = TextComponent.fromLegacyText(color(split.get(2)));
fixLegacyFormat(components);
HoverEvent event = new HoverEvent(action, components);
components = TextComponent.fromLegacyText(color(split.get(3)));
for (BaseComponent c : components) c.setHoverEvent(event);
if (hoverText == null) hoverText = Lists.newArrayList();
hoverText.add(components);
message = message.replace(m.group(0), "%hover%");
} else if (split.get(0).equalsIgnoreCase("click")) {
ClickEvent.Action action = EnumUtil
.matchEnumValue(ClickEvent.Action.class, split.get(1));
if (clickText == null) clickText = Lists.newArrayList();
ClickEvent event = new ClickEvent(action, split.get(2));
BaseComponent[] components = TextComponent.fromLegacyText(color(split.get(3)));
for (BaseComponent c : components) c.setClickEvent(event);
clickText.add(components);
message = message.replace(m.group(0), "%click%");
}
}
List<BaseComponent> components = Lists.newArrayList();
m = SPLIT_FORMAT_PATTERN.matcher(message);
BaseComponent c;
components.add(new TextComponent(""));
List<ChatColor> formatting = new ArrayList<>();
while (m.find()) {
String s = m.group(1);
ChatColor color = null;
if (s.length() > 0) {
char format = s.charAt(0);
color = getByChar(format);
if (color != null) {
s = s.substring(1);
addFormat(formatting, color);
}
}
int end = 0;
Matcher m1 = REPLACER_PATTERN.matcher(s);
while (m1.find()) {
c = new TextComponent(m1.group(1));
if (color != null) format(c, formatting);
last(components).addExtra(c);
components.add(c);
if (m1.group(2).equals("hover") && hoverText != null && !hoverText.isEmpty()) {
for (BaseComponent b : hoverText.get(0)) {
if (color != null) format(b, formatting);
last(components).addExtra(b);
components.add(b);
}
hoverText.remove(0);
} else if (m1.group(2).equals("click") && clickText != null && !clickText
.isEmpty()) {
for (BaseComponent b : clickText.get(0)) {
if (color != null) format(b, formatting);
last(components).addExtra(b);
components.add(b);
}
clickText.remove(0);
}
end = m1.end();
}
if (m1.groupCount() > 1) {
s = s.substring(Math.min(s.length(), end));
if (s.length() > 0) {
c = new TextComponent(s);
if (color != null) format(c, formatting);
last(components).addExtra(c);
components.add(c);
c.setHoverEvent(null);
c.setClickEvent(null);
}
}
}
return components.get(0);
}
private static void addFormat(List<ChatColor> formatting, ChatColor format) {
boolean contained = formatting.remove(format);
if (formatting.stream().filter(MessageUtil::isColor).findAny().isPresent()
|| (!isColor(format) && contained)) {
formatting.clear();
}
formatting.add(format);
}
private static boolean isColor(ChatColor format) {
switch (format) {
case BOLD:
case STRIKETHROUGH:
case ITALIC:
case MAGIC:
case RESET:
case UNDERLINE:
return false;
default:
return true;
}
}
public static String translate(CommandSender recipient, String key) {
if (key == null) return "undefined";
ConfigurationSection messages = CraftFX.instance().getMessages(Locale.ENGLISH);
// todo: Could add support for multiple languages based on sender's geolocation/language prefs
// todo: support for null recipient ^
if (messages.contains(key)) {
return messages.getString(key);
}
return key;
}
public static String format(String pattern, Object... arguments) {
return color(MessageFormat.format(pattern, arguments));
}
private static void format(BaseComponent component, List<ChatColor> formatting) {
for (ChatColor format : formatting) {
if (isColor(format) || format.equals(RESET)) {
component.setColor(format);
}
}
component.setBold(formatting.contains(BOLD));
component.setItalic(formatting.contains(ITALIC));
component.setObfuscated(formatting.contains(MAGIC));
component.setStrikethrough(formatting.contains(STRIKETHROUGH));
component.setUnderlined(formatting.contains(UNDERLINE));
}
private static <T> T last(List<T> list) {
return list.get(list.size() - 1);
}
private static void fixLegacyFormat(BaseComponent[] components) {
Arrays.stream(components).forEach(c -> {
Optional.of(c.isBoldRaw() != null).ifPresent(c::setBold);
Optional.of(c.isItalicRaw() != null).ifPresent(c::setItalic);
Optional.of(c.isObfuscatedRaw() != null).ifPresent(c::setObfuscated);
Optional.of(c.isStrikethroughRaw() != null).ifPresent(c::setStrikethrough);
Optional.of(c.isUnderlinedRaw() != null).ifPresent(c::setUnderlined);
});
}
public static String replace(String message, CommandSender sender) {
Matcher m = REPLACER_PATTERN.matcher(message);
String result = "";
int end = 0;
while (m.find()) {
result += m.group(1);
if (m.group(2).equalsIgnoreCase("name")) result += sender.getName();
end = m.end();
}
return result + message.substring(end);
}
}