001/*
002 * PermissionsEx
003 * Copyright (C) zml and PermissionsEx contributors
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 *    http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package ca.stellardrift.permissionsex.minecraft.command;
018
019import ca.stellardrift.permissionsex.context.ContextValue;
020import ca.stellardrift.permissionsex.impl.util.PCollections;
021import cloud.commandframework.Command;
022import cloud.commandframework.arguments.CommandArgument;
023import cloud.commandframework.arguments.StaticArgument;
024import net.kyori.adventure.text.Component;
025import net.kyori.adventure.text.format.NamedTextColor;
026import net.kyori.adventure.text.format.TextColor;
027import net.kyori.adventure.util.ComponentMessageThrowable;
028import org.checkerframework.checker.nullness.qual.Nullable;
029
030import java.util.List;
031import java.util.Map;
032import java.util.Set;
033
034import static net.kyori.adventure.text.Component.text;
035
036/**
037 * Message creators for use in command output.
038 */
039public final class Formats {
040    public static final Component COMMA = text(",");
041    private static final Component BOOL_TRUE = Messages.FORMATTER_BOOLEAN_TRUE.bTr()
042        .color(NamedTextColor.GREEN)
043        .build();
044
045    private static final Component BOOL_FALSE = Messages.FORMATTER_BOOLEAN_FALSE.bTr()
046        .color(NamedTextColor.RED)
047        .build();
048
049    private Formats() {
050    }
051
052    /**
053     * Represent a permission and its value in a user-visible way.
054     *
055     * The output will appear as {@code <permisison>=<value>}.
056     *
057     * @param permission the permission
058     * @param value its value, listed and used for colouring
059     * @return the formatted permission
060     * @since 2.0.0
061     */
062    public static Component permission(final String permission, final int value) {
063        final NamedTextColor color;
064        if (value > 0) {
065            color = NamedTextColor.GREEN;
066        } else if (value < 0) {
067            color = NamedTextColor.RED;
068        } else {
069            color = NamedTextColor.GRAY;
070        }
071        return text()
072                .append(text(permission, color))
073                .append(MessageFormatter.EQUALS_SIGN)
074                .append(text(value))
075                .build();
076    }
077
078    public static Component permissionValue(final int value) {
079        final NamedTextColor color;
080        if (value > 0) {
081            color = NamedTextColor.GREEN;
082        } else if (value < 0) {
083            color = NamedTextColor.RED;
084        } else {
085            color = NamedTextColor.GRAY;
086        }
087        return text(value, color);
088    }
089
090    public static Component option(final String option, final String value) {
091        return text()
092                .content(option)
093                .append(MessageFormatter.EQUALS_SIGN)
094                .append(text(value))
095                .build();
096    }
097
098    public static Component bool(final boolean value) {
099        return value ? BOOL_TRUE : BOOL_FALSE;
100    }
101
102    public static Component contexts(final Set<ContextValue<?>> contexts) {
103        if (contexts.isEmpty()) {
104            return Messages.COMMON_ARGS_CONTEXT_GLOBAL.tr();
105        } else {
106            return Component.join(COMMA, contexts);
107        }
108    }
109
110    /**
111     * Format the specified command, filling in arguments as provided until the first unset
112     * non-static argument is encountered.
113     *
114     * @param command the command to format
115     * @param placeholders arguments to fill in
116     * @return the formatted command
117     */
118    public static String formatCommand(final Command<?> command, final Map<CommandArgument<?, ?>, String> placeholders) {
119        return formatCommand(command.getArguments(), placeholders);
120    }
121
122    /**
123     * Format the specified command, filling in arguments as provided until the first unset
124     * non-static argument is encountered.
125     *
126     * @param command the command to format
127     * @param placeholders arguments to fill in
128     * @return the formatted command
129     */
130    public static String formatCommand(final Command.Builder<?> command, final Map<CommandArgument<?, ?>, String> placeholders) {
131        return formatCommand(command.build().getArguments(), placeholders); // TODO: can we do this without building the command?
132    }
133
134    /**
135     * Format the specified command, filling in arguments as provided until the first unset
136     * non-static argument is encountered.
137     *
138     * @param arguments the base arguments
139     * @param placeholders arguments to fill in
140     * @return the formatted command
141     */
142    private static String formatCommand(final List<? extends CommandArgument<?, ?>> arguments, final Map<CommandArgument<?, ?>, String> placeholders) {
143        final StringBuilder builder = new StringBuilder("/");
144        for (final CommandArgument<?, ?> argument : arguments) {
145            if (argument instanceof StaticArgument<?>) {
146                builder.append(argument.getName());
147            } else {
148                final @Nullable String value = placeholders.get(argument);
149                if (value == null) {
150                    break;
151                }
152                builder.append(value);
153            }
154            builder.append(" ");
155        }
156        return builder.toString();
157    }
158
159    public static String formatCommand(
160            final Command<?> command,
161            final CommandArgument<?, ?> arg1, final String val1
162    ) {
163        return formatCommand(command, PCollections.map(arg1, val1));
164    }
165
166    public static String formatCommand(
167            final Command<?> command,
168            final CommandArgument<?, ?> arg1, final String val1,
169            final CommandArgument<?, ?> arg2, final String val2
170    ) {
171        return formatCommand(command, PCollections.<CommandArgument<?, ?>, String>map(arg1, val1).plus(arg2, val2));
172    }
173
174    public static Component message(final Throwable throwable) {
175        if (throwable instanceof ComponentMessageThrowable) {
176            final @Nullable Component msg =  ((ComponentMessageThrowable) throwable).componentMessage();
177            return msg == null ? text("null") : msg;
178        } else {
179            return text(throwable.getMessage());
180        }
181    }
182
183    public static TextColor lerp(final float pct, final TextColor from, final TextColor to) {
184        // https://github.com/KyoriPowered/adventure/pull/215
185        return TextColor.color(
186            Math.round(from.red() + pct * (to.red() - from.red())),
187            Math.round(from.green() + pct * (to.green() - from.green())),
188            Math.round(from.blue() + pct * (to.blue() - from.blue()))
189        );
190    }
191
192}