package com.laytonsmith.core.functions;
import com.laytonsmith.PureUtilities.Common.StreamUtils;
import com.laytonsmith.PureUtilities.Common.StringUtils;
import com.laytonsmith.PureUtilities.TermColors;
import com.laytonsmith.abstraction.MCCommandSender;
import com.laytonsmith.abstraction.MCPlayer;
import com.laytonsmith.abstraction.MCServer;
import com.laytonsmith.abstraction.enums.MCChatColor;
import com.laytonsmith.annotations.api;
import com.laytonsmith.annotations.noboilerplate;
import com.laytonsmith.core.CHVersion;
import com.laytonsmith.core.Optimizable;
import com.laytonsmith.core.Static;
import com.laytonsmith.core.constructs.CNull;
import com.laytonsmith.core.constructs.CString;
import com.laytonsmith.core.constructs.CVoid;
import com.laytonsmith.core.constructs.Construct;
import com.laytonsmith.core.constructs.Target;
import com.laytonsmith.core.environments.CommandHelperEnvironment;
import com.laytonsmith.core.environments.Environment;
import com.laytonsmith.core.exceptions.CRE.CRECastException;
import com.laytonsmith.core.exceptions.CRE.CREInsufficientArgumentsException;
import com.laytonsmith.core.exceptions.CRE.CRENullPointerException;
import com.laytonsmith.core.exceptions.CRE.CREPlayerOfflineException;
import com.laytonsmith.core.exceptions.CRE.CREThrowable;
import com.laytonsmith.core.exceptions.CancelCommandException;
import com.laytonsmith.core.exceptions.ConfigRuntimeException;
import java.util.Collections;
import java.util.EnumSet;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
/**
*
*/
public class Echoes {
public static String docs(){
return "These functions allow you to echo information to the screen";
}
@api(environments={CommandHelperEnvironment.class})
@noboilerplate
public static class die extends AbstractFunction implements Optimizable {
@Override
public Integer [] numArgs() {
return new Integer[]{Integer.MAX_VALUE};
}
@Override
public Construct exec(Target t, Environment env, Construct... args) throws CancelCommandException{
if(args.length == 0){
throw new CancelCommandException("", t);
}
StringBuilder b = new StringBuilder();
for (Construct arg : args) {
b.append(arg.val());
}
try{
if(env.hasEnv(CommandHelperEnvironment.class)){
Static.SendMessage(env.getEnv(CommandHelperEnvironment.class).GetCommandSender(), b.toString(), t);
} else {
String mes = Static.MCToANSIColors(b.toString());
if(mes.contains("\033")){
//We have terminal colors, we need to reset them at the end
mes += TermColors.reset();
}
StreamUtils.GetSystemOut().println(mes);
}
} finally{
throw new CancelCommandException("", t);
}
}
@Override
public Class<? extends CREThrowable>[] thrown(){
return new Class[]{};
}
@Override
public String getName(){ return "die"; }
@Override
public String docs(){
return "nothing {[var1, var2...,]} Kills the command immediately, without completing it. A message is optional, but if provided, displayed to the user.";
}
@Override
public boolean isRestricted() {
return false;
}
@Override
public CHVersion since() {
return CHVersion.V3_0_1;
}
@Override
public Boolean runAsync(){
return false;
}
@Override
public Set<OptimizationOption> optimizationOptions() {
return EnumSet.of(
OptimizationOption.TERMINAL
);
}
}
//Technically it needs CommandHelperEnvironment, but we have special exception handling in case we're running
//in cmdline mode.
@api(environments={})
@noboilerplate
public static class msg extends AbstractFunction{
@Override
public String getName() {
return "msg";
}
@Override
public Integer[] numArgs() {
return new Integer[]{Integer.MAX_VALUE};
}
@Override
public Construct exec(final Target t, Environment env, final Construct... args) throws CancelCommandException, ConfigRuntimeException {
StringBuilder b = new StringBuilder();
for(int i = 0; i < args.length; i++){
b.append(args[i].val());
}
if(env.hasEnv(CommandHelperEnvironment.class)){
final MCCommandSender p = env.getEnv(CommandHelperEnvironment.class).GetCommandSender();
Static.SendMessage(p, b.toString(), t);
} else {
String mes = Static.MCToANSIColors(b.toString());
if(mes.contains("\033")){
//We have terminal colors, we need to reset them at the end
mes += TermColors.reset();
}
StreamUtils.GetSystemOut().println(mes);
StreamUtils.GetSystemOut().flush();
}
return CVoid.VOID;
}
@Override
public Class<? extends CREThrowable>[] thrown(){
return new Class[]{CREPlayerOfflineException.class};
}
@Override
public String docs() {
return "void {var1, [var2...]} Echoes a message to the player running the command";
}
@Override
public boolean isRestricted() {
return false;
}
@Override
public CHVersion since() {
return CHVersion.V3_0_1;
}
@Override
public Boolean runAsync(){
return false;
}
}
@api public static class tmsg extends AbstractFunction{
@Override
public String getName() {
return "tmsg";
}
@Override
public Integer[] numArgs() {
return new Integer[]{Integer.MAX_VALUE};
}
@Override
public Construct exec(Target t, Environment env, Construct... args) throws CancelCommandException, ConfigRuntimeException {
if(args.length < 2){
throw new CREInsufficientArgumentsException("You must send at least 2 arguments to tmsg", t);
}
MCCommandSender p;
if (Static.getConsoleName().equals(args[0].val())) {
p = Static.getServer().getConsole();
} else {
p = Static.GetPlayer(args[0], t);
}
if(p == null){
throw new CREPlayerOfflineException("The player " + args[0].val() + " is not online", t);
}
StringBuilder b = new StringBuilder();
for(int i = 1; i < args.length; i++){
b.append(args[i].val());
}
Static.SendMessage(p, b.toString(), t);
// int start = 0;
// String s = b.toString();
// while(true){
// if(start >= s.length()) break;
// p.sendMessage(s.substring(start, start + 100 >= s.length()?s.length():start + 100));
// start += 100;
// }
return CVoid.VOID;
}
@Override
public String docs() {
return "void {player, msg, [...]} Displays a message on the specified players screen, similar to msg, but targets a specific user.";
}
@Override
public Class<? extends CREThrowable>[] thrown(){
return new Class[]{CREPlayerOfflineException.class, CREInsufficientArgumentsException.class};
}
@Override
public boolean isRestricted() {
return true;
}
@Override
public CHVersion since() {
return CHVersion.V3_0_1;
}
@Override
public Boolean runAsync(){
return false;
}
}
@api public static class color extends AbstractFunction implements Optimizable {
private Map<String, CString> colors = new TreeMap<String, CString>();
private static final String symbols = "0123456789abcdefABCDEFmMnNoOlLkKrR";
public static final Set<Character> COLOR_SYMBOLS;
static {
Set<Character> temp = new TreeSet<Character>();
for(Character c : symbols.toCharArray()){
temp.add(c);
}
COLOR_SYMBOLS = Collections.unmodifiableSet(temp);
}
@Override
public String getName() {
return "color";
}
@Override
public Integer[] numArgs() {
return new Integer[]{1};
}
@Override
public Construct exec(Target t, Environment env, Construct... args) throws CancelCommandException, ConfigRuntimeException {
String color = null;
String val = args[0].nval();
if (val == null) {
return new CString(MCChatColor.WHITE.toString(), t);
}
if(colors.containsKey(val)){
return colors.get(val);
}
try{
color = MCChatColor.valueOf(val.toUpperCase()).toString();
} catch(IllegalArgumentException e){}
String a = val.toLowerCase();
if(a.equals("10")){
a = "a";
} else if(a.equals("11")){
a = "b";
} else if(a.equals("12")){
a = "c";
} else if(a.equals("13")){
a = "d";
} else if(a.equals("14")){
a = "e";
} else if(a.equals("15")){
a = "f";
} else if(a.equals("random")){
a = "k";
} else if(a.equals("bold")){
a = "l";
} else if(a.equals("strike") || a.equals("strikethrough")){
a = "m";
} else if(a.equals("underline") || a.equals("underlined")){
a = "n";
} else if(a.equals("italic") || a.equals("italics")){
a = "o";
} else if(a.equals("plain white") || a.equals("plainwhite") || a.equals("plain_white")){
a = "r";
}
//////////////////////////////////////////////////////////////
// IMPORTANT //
// Be sure to update COLOR_SYMBOLS if this list is updated! //
//////////////////////////////////////////////////////////////
if("".equals(a.trim())){
//If the value is empty string, set the color to white.
color = MCChatColor.WHITE.toString();
}
if(color == null){
try{
Character p = String.valueOf(a).charAt(0);
MCChatColor cc = MCChatColor.getByChar(p);
if(cc == null){
cc = MCChatColor.WHITE;
}
color = cc.toString();
} catch(NumberFormatException e){}
}
if(color == null){
color = MCChatColor.WHITE.toString();
}
//Until we get a compilation environment going, this must be removed so we can optimize it out.
// if(env.GetCustom("cmdline") instanceof Boolean && (Boolean)env.GetCustom("cmdline") == true){
// color = Static.MCToANSIColors(color);
// }
CString ret = new CString(color, t);
colors.put(val, ret);
return ret;
}
@Override
public String docs() {
String [] b = new String[MCChatColor.values().length];
for(int i = 0; i < b.length; i++){
b[i] = MCChatColor.values()[i].name(); Enum e = null;
}
return "string {name} Returns the color modifier given a color name. If the given color name isn't valid, white is used instead."
+ " The list of valid colors is: " + StringUtils.Join(b, ", ", ", or ") + ", in addition the integers 0-15 will work,"
+ " or the hex numbers from 0-F, and k, l, m, n, o, and r, which represent styles. Unlike manually putting in the color symbol,"
+ " using this function will return the platform's color code, so if you are wanting to keep your scripts platform independant,"
+ " it is a much better idea to use this function as opposed to hard coding your own color codes.";
}
@Override
public Class<? extends CREThrowable>[] thrown(){
return new Class[]{};
}
@Override
public boolean isRestricted() {
return true;
}
@Override
public CHVersion since() {
return CHVersion.V3_0_1;
}
@Override
public Boolean runAsync(){
return null;
}
@Override
public Set<OptimizationOption> optimizationOptions() {
return EnumSet.of(
OptimizationOption.CONSTANT_OFFLINE,
OptimizationOption.CACHE_RETURN
);
}
}
@api public static class strip_colors extends AbstractFunction{
@Override
public String getName() {
return "strip_colors";
}
@Override
public Integer[] numArgs() {
return new Integer[]{1};
}
@Override
public String docs() {
return "string {toStrip} Strips all the color codes from a given string";
}
@Override
public Class<? extends CREThrowable>[] thrown() {
return new Class[]{};
}
@Override
public boolean isRestricted() {
return false;
}
@Override
public CHVersion since() {
return CHVersion.V3_3_0;
}
@Override
public Boolean runAsync() {
return false;
}
@Override
public Construct exec(Target t, Environment environment, Construct... args) throws ConfigRuntimeException {
return new CString(MCChatColor.stripColor(args[0].val()), t);
}
}
@api(environments={CommandHelperEnvironment.class})
public static class chat extends AbstractFunction{
@Override
public String getName() {
return "chat";
}
@Override
public Integer[] numArgs() {
return new Integer[]{1};
}
@Override
public Construct exec(final Target t, final Environment env, Construct... args) throws CancelCommandException, ConfigRuntimeException {
MCPlayer p = env.getEnv(CommandHelperEnvironment.class).GetPlayer();
if(p != null){
p.chat(args[0].val());
} else {
throw new CREPlayerOfflineException("Console cannot chat. Use something like broadcast() instead.", t);
}
return CVoid.VOID;
}
@Override
public String docs() {
return "void {string} Echoes string to the chat, as if the user simply typed something into the chat bar. This function cannot"
+ " be run from console, a PlayerOfflineException is thrown if attempted. Use broadcast() instead.";
}
@Override
public Class<? extends CREThrowable>[] thrown(){
return new Class[]{CREPlayerOfflineException.class};
}
@Override
public boolean isRestricted() {
return true;
}
@Override
public CHVersion since() {
return CHVersion.V3_0_1;
}
@Override
public Boolean runAsync(){
return false;
}
}
@api public static class chatas extends AbstractFunction{
@Override
public String getName() {
return "chatas";
}
@Override
public Integer[] numArgs() {
return new Integer[]{2};
}
@Override
public String docs() {
return "void {player, msg} Sends a chat message to the server, as the given player. Otherwise the same as the chat"
+ " function";
}
@Override
public Class<? extends CREThrowable>[] thrown(){
return new Class[]{CREPlayerOfflineException.class};
}
@Override
public boolean isRestricted() {
return true;
}
@Override
public CHVersion since() {
return CHVersion.V3_0_2;
}
@Override
public Construct exec(Target t, Environment env, Construct... args) throws CancelCommandException, ConfigRuntimeException {
final MCPlayer player = Static.GetPlayer(args[0], t);
Static.AssertPlayerNonNull(player, t);
player.chat(args[1].val());
return CVoid.VOID;
}
@Override
public Boolean runAsync(){
return false;
}
}
@api public static class broadcast extends AbstractFunction{
@Override
public String getName() {
return "broadcast";
}
@Override
public Integer[] numArgs() {
return new Integer[]{1, 2};
}
@Override
public String docs() {
return "void {message, [permission]} Broadcasts a message to all players on the server."
+ " If permission is given, only players with that permission will see the broadcast.";
}
@Override
public Class<? extends CREThrowable>[] thrown(){
return new Class[]{CRENullPointerException.class};
}
@Override
public boolean isRestricted() {
return true;
}
@Override
public CHVersion since() {
return CHVersion.V3_0_1;
}
@Override
public Construct exec(Target t, Environment env, Construct... args) throws CancelCommandException, ConfigRuntimeException {
if(args[0] instanceof CNull){
throw new CRENullPointerException("Trying to broadcast null won't work", t);
}
final MCServer server = Static.getServer();
String permission = null;
if (args.length == 2 && !(args[1] instanceof CNull)) {
permission = args[1].val();
}
if (permission == null) {
server.broadcastMessage(args[0].val());
} else {
server.broadcastMessage(args[0].val(), permission);
}
return CVoid.VOID;
}
@Override
public Boolean runAsync(){
return false;
}
}
@api
@noboilerplate
public static class console extends AbstractFunction{
@Override
public String getName() {
return "console";
}
@Override
public Integer[] numArgs() {
return new Integer[]{1, 2};
}
@Override
public String docs() {
return "void {message, [prefix]} Logs a message to the console. If prefix is true, prepends \"CommandHelper:\""
+ " to the message. Default is true.";
}
@Override
public Class<? extends CREThrowable>[] thrown(){
return new Class[]{CRECastException.class};
}
@Override
public boolean isRestricted() {
return true;
}
@Override
public CHVersion since() {
return CHVersion.V3_0_2;
}
@Override
public Construct exec(Target t, Environment env, Construct... args) throws CancelCommandException, ConfigRuntimeException {
String mes = args[0].val();
boolean prefix = true;
if(args.length > 1){
prefix = Static.getBoolean(args[1]);
}
mes = (prefix?"CommandHelper: ":"") + Static.MCToANSIColors(mes);
if(mes.contains("\033")){
//We have terminal colors, we need to reset them at the end
mes += TermColors.reset();
}
StreamUtils.GetSystemOut().println(mes);
return CVoid.VOID;
}
@Override
public Boolean runAsync(){
return null;
}
}
@api
public static class colorize extends AbstractFunction implements Optimizable {
@Override
public Class<? extends CREThrowable>[] thrown() {
return null;
}
@Override
public boolean isRestricted() {
return true;
}
@Override
public Boolean runAsync() {
return null;
}
color color = new color();
@Override
public Construct exec(Target t, Environment environment, Construct... args) throws ConfigRuntimeException {
Construct text = args[0];
String symbol = "&";
if(args.length == 2){
symbol = args[1].val();
}
if(text instanceof CString){
String stext = text.val();
StringBuilder b = new StringBuilder();
int sl = symbol.length();
for(int i = 0; i < stext.length(); i++){
if(i + sl >= stext.length()){
if(i < stext.length()){
b.append(stext.substring(i));
break;
}
} else {
String subsequence1 = stext.substring(i, i + sl);
if(!symbol.equals(subsequence1)){
b.append(stext.charAt(i));
continue;
}
try{
String subsequence2 = stext.substring(i + sl, i + (sl * 2));
if(subsequence2.equals(subsequence1)){
b.append(subsequence1);
i += (sl * 2) - 1;
continue;
}
} catch(IndexOutOfBoundsException e){
//Ignored, it just means there aren't enough characters to do a second
//subsequence
}
Character c;
try{
c = stext.charAt(i + sl);
} catch(IndexOutOfBoundsException e){
b.append(stext.charAt(i + sl - 1));
break;
}
if(color.COLOR_SYMBOLS.contains(c)){
b.append(color.exec(t, environment, new CString(c, t)));
i += sl;
continue;
} else {
b.append(subsequence1);
i += sl - 1;
continue;
}
}
}
return new CString(b.toString(), t);
} else {
return text;
}
}
@Override
public String getName() {
return "colorize";
}
@Override
public Integer[] numArgs() {
return new Integer[]{1, 2};
}
@Override
public String docs() {
return "mixed {text, [symbol]} Replaces all the colorizable text in the string. For instance,"
+ " colorize('&aText') would be equivalent to (color('a').'Text'). By default, the"
+ " symbol is '&', but that can be any arbitrary string that you specify. If text is not"
+ " a string, that value is simply returned. If you need to \"escape\" a symbol, (that is"
+ " have a literal symbol followed by a letter that is a valid color) just repeat the symbol"
+ " twice, for instance '&&c' would return a literal '&c' instead of a red modifier.";
}
@Override
public CHVersion since() {
return CHVersion.V3_3_1;
}
@Override
public Set<OptimizationOption> optimizationOptions() {
return EnumSet.of(OptimizationOption.CONSTANT_OFFLINE);
}
}
}