/**
* PermissionsEx
* Copyright (C) zml and PermissionsEx contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ninja.leaping.permissionsex.command;
import com.google.common.collect.ImmutableSet;
import com.google.common.util.concurrent.Futures;
import ninja.leaping.permissionsex.PermissionsEx;
import ninja.leaping.permissionsex.data.SubjectDataReference;
import ninja.leaping.permissionsex.rank.RankLadder;
import ninja.leaping.permissionsex.util.Util;
import ninja.leaping.permissionsex.util.command.ButtonType;
import ninja.leaping.permissionsex.util.command.ChildCommands;
import ninja.leaping.permissionsex.util.command.CommandContext;
import ninja.leaping.permissionsex.util.command.CommandException;
import ninja.leaping.permissionsex.util.command.CommandExecutor;
import ninja.leaping.permissionsex.util.command.CommandSpec;
import ninja.leaping.permissionsex.util.command.Commander;
import ninja.leaping.permissionsex.util.command.args.CommandElement;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicReference;
import static ninja.leaping.permissionsex.util.Translations.t;
import static ninja.leaping.permissionsex.util.command.args.GameArguments.rankLadder;
import static ninja.leaping.permissionsex.util.command.args.GameArguments.subject;
import static ninja.leaping.permissionsex.util.command.args.GenericArguments.*;
public class RankingCommands {
public static CommandSpec getPromoteCommand(PermissionsEx pex) {
return CommandSpec.builder()
.setAliases("promote", "prom")
.setDescription(t("Promote a subject on the given ladder"))
.setArguments(Util.contextTransientFlags().buildWith(seq(subject(t("subject"), pex), optional(rankLadder(t("ladder"), pex)))))
.setExecutor(new PermissionsExExecutor(pex) {
@Override
public <TextType> void execute(Commander<TextType> src, CommandContext args) throws CommandException {
CompletableFuture<RankLadder> ladderF = args.hasAny("ladder") ? args.getOne("ladder") : pex.getLadders().get("default", null);
SubjectDataReference ref = getDataRef(src, args, "permissionsex.promote"); // ." + ladderF); // TODO: Re-add permissions checks for ladders
Set<Map.Entry<String, String>> contexts = ImmutableSet.copyOf(args.<Map.Entry<String, String>>getAll("context"));
final AtomicReference<RankLadder> ladderName = new AtomicReference<>();
messageSubjectOnFuture(ladderF.thenCompose(ladder -> {
ladderName.set(ladder);
return ref.update(old -> ladder.promote(contexts, old));
})
.thenAccept(res -> {
if (res.getNew() == res.getOld()) {
throw new RuntimeCommandException(t("%s was already at the top of ladder %s", src.fmt().subject(ref), src.fmt().ladder(ladderName.get())));
}
}), src, () -> t("Promoted %s on ladder %s", src.fmt().subject(ref), src.fmt().hl(src.fmt().ladder(ladderName.get()))));
}
})
.build();
}
public static CommandSpec getDemoteCommand(PermissionsEx pex) {
return CommandSpec.builder()
.setAliases("demote", "dem")
.setDescription(t("Demote a subject on the given ladder"))
.setArguments(Util.contextTransientFlags().buildWith(seq(subject(t("subject"), pex), optional(rankLadder(t("ladder"), pex)))))
.setExecutor(new PermissionsExExecutor(pex) {
@Override
public <TextType> void execute(Commander<TextType> src, CommandContext args) throws CommandException {
CompletableFuture<RankLadder> ladderF = args.hasAny("ladder") ? args.getOne("ladder") : pex.getLadders().get("default", null);
SubjectDataReference ref = getDataRef(src, args, "permissionsex.demote"); //." + ladder);
Set<Map.Entry<String, String>> contexts = ImmutableSet.copyOf(args.<Map.Entry<String, String>>getAll("context"));
final AtomicReference<RankLadder> ladderName = new AtomicReference<>();
messageSubjectOnFuture(ladderF.thenCompose(ladder -> {
return ref.update(old -> ladder.demote(contexts, old));}).thenAccept(res -> {
if (res.getNew() == res.getOld()) {
throw new RuntimeCommandException(t("%s was not on ladder %s", src.fmt().subject(ref), src.fmt().ladder(ladderName.get())));
}
}), src, () -> t("Demoted %s on ladder %s", src.fmt().subject(ref), src.fmt().hl(src.fmt().ladder(ladderName.get()))));
}
})
.build();
}
private static <TextType> TextType deleteButton(Commander<TextType> cmd, RankLadder rank, Map.Entry<String, String> subject) {
return cmd.fmt().button(ButtonType.NEGATIVE, t("-"), t("Remove this rank from the ladder"), "/pex rank " + rank.getName() + " remove " + subject.getKey() + " " + subject.getValue(), true);
}
private static <TextType> TextType moveDownButton(Commander<TextType> cmd, RankLadder rank, Map.Entry<String, String> subject) {
return cmd.fmt().button(ButtonType.NEUTRAL, t("\u25bc"), t("Move this rank to a lower position in the ladder"), "/pex rank " + rank.getName() + " add -r -1 " + subject.getKey() + " " + subject.getValue(), true);
}
private static <TextType> TextType moveUpButton(Commander<TextType> cmd, RankLadder rank, Map.Entry<String, String> subject) {
return cmd.fmt().button(ButtonType.NEUTRAL, t("\u25b2"), t("Move this rank to a higher position in the ladder"), "/pex rank " + rank.getName() + " add -r 1 " + subject.getKey() + " " + subject.getValue(), true);
}
public static CommandSpec getRankingCommand(PermissionsEx pex) {
final CommandElement arg = ChildCommands.args(getRankAddChildCommand(pex), getRankRemoveCommand(pex));
return CommandSpec.builder()
.setAliases("ranking", "rank")
.setDescription(t("Commands to modify ranking"))
.setArguments(seq(rankLadder(t("ladder"), pex), optional(arg)))
.setExecutor(ChildCommands.optionalExecutor(arg, new CommandExecutor() {
@Override
public <TextType> void execute(Commander<TextType> src, CommandContext args) throws CommandException {
final RankLadder ladder = Futures.getUnchecked(args.<CompletableFuture<RankLadder>>getOne("ladder"));
List<TextType> ranksList = new ArrayList<>();
List<? extends Map.Entry<String, String>> rawRanks = new ArrayList<>(ladder.getRanks());
Collections.reverse(rawRanks);
if (rawRanks.size() == 1) {
ranksList.add(src.fmt().combined(src.fmt().subject(rawRanks.get(0)), deleteButton(src, ladder, rawRanks.get(0))));
} else if (rawRanks.size() == 0) {
throw new CommandException(t("No ranks in ladder %s", src.fmt().ladder(ladder)));
} else {
for (int i = 0; i < rawRanks.size(); ++i) {
Map.Entry<String, String> rank = rawRanks.get(i);
if (i == 0) {
ranksList.add(src.fmt().combined(src.fmt().subject(rawRanks.get(i)),
" ", moveDownButton(src, ladder, rank),
" ", deleteButton(src, ladder, rank)));
} else if (i == rawRanks.size() - 1) {
ranksList.add(src.fmt().combined(src.fmt().subject(rawRanks.get(i)),
" ", moveUpButton(src, ladder, rank),
" ", deleteButton(src, ladder, rank)));
} else {
ranksList.add(src.fmt().combined(src.fmt().subject(rawRanks.get(i)),
" ", moveDownButton(src, ladder, rank),
" ", moveUpButton(src, ladder, rank),
" ", deleteButton(src, ladder, rank)));
}
}
}
src.msgPaginated(t("Rank ladder %s %s", ladder.getName(), src.fmt().button(ButtonType.POSITIVE, t("+"), t("Add a rank to this ladder"), "/pex rank " + ladder.getName() + " add ", false)),
t("Ranks are sorted from highest to lowest"), ranksList);
}
}))
.build();
}
private static CommandSpec getRankAddChildCommand(PermissionsEx pex) {
return CommandSpec.builder()
.setAliases("add", "+")
.setArguments(flags()
.flag("r", "-relative")
.setUnknownShortFlagBehavior(UnknownFlagBehavior.IGNORE)
.buildWith(seq(optional(integer(t("position"))), subject(t("subject"), pex, PermissionsEx.SUBJECTS_GROUP))))
.setExecutor(new PermissionsExExecutor(pex) {
@Override
public <TextType> void execute(Commander<TextType> src, CommandContext args) throws CommandException {
final RankLadder ladder = Futures.getUnchecked(args.<CompletableFuture<RankLadder>>getOne("ladder"));
Map.Entry<String, String> toAdd = args.getOne("subject");
checkSubjectPermission(src, toAdd, "permissionsex.rank.add." + ladder.getName());
Integer position = args.getOne("position");
if (position != null) {
int addPosition = position;
if (args.hasAny("r")) {
int currentIndex = ladder.indexOfRank(toAdd);
if (currentIndex == -1) {
throw new CommandException(t("Cannot do a relative move on a rank that is not in the ladder"));
}
addPosition = currentIndex + addPosition > 1 ? addPosition + 1 : addPosition; // If we are adding to later, we need to add after the next rank (otherwise we end up staying in the same place)
}
messageSubjectOnFuture(pex.getLadders().set(ladder.getName(), ladder.addRankAt(toAdd, addPosition)), src, t("Successfully added %s to ladder %s at position %s", src.fmt().subject(toAdd), src.fmt().ladder(ladder), addPosition));
} else {
messageSubjectOnFuture(pex.getLadders().set(ladder.getName(), ladder.addRank(toAdd)), src, t("Successfully added %s to ladder %s", src.fmt().subject(toAdd), src.fmt().ladder(ladder)));
}
}
})
.build();
}
private static CommandSpec getRankRemoveCommand(PermissionsEx pex) {
return CommandSpec.builder()
.setAliases("remove", "rem", "-")
.setArguments(subject(t("subject"), pex, PermissionsEx.SUBJECTS_GROUP))
.setExecutor(new PermissionsExExecutor(pex) {
@Override
public <TextType> void execute(Commander<TextType> src, CommandContext args) throws CommandException {
final RankLadder ladder = Futures.getUnchecked(args.<CompletableFuture<RankLadder>>getOne("ladder"));
Map.Entry<String, String> toRemove = args.getOne("subject");
checkSubjectPermission(src, toRemove, "permissionsex.rank.remove." + ladder.getName());
RankLadder newLadder = ladder.removeRank(toRemove);
if (newLadder == ladder) {
throw new CommandException(t("Rank %s was not in ladder %s", src.fmt().subject(toRemove), src.fmt().ladder(ladder)));
} else {
messageSubjectOnFuture(pex.getLadders().set(ladder.getName(), newLadder), src, t("Successfully removed %s from ladder %s", src.fmt().subject(toRemove), src.fmt().ladder(ladder)));
}
}
})
.build();
}
}