package tc.oc.commons.bukkit.trophies;
import javax.inject.Inject;
import com.google.common.collect.ImmutableSet;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.sk89q.minecraft.util.commands.Command;
import com.sk89q.minecraft.util.commands.CommandContext;
import com.sk89q.minecraft.util.commands.CommandException;
import com.sk89q.minecraft.util.commands.CommandPermissions;
import com.sk89q.minecraft.util.commands.NestedCommand;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.chat.TranslatableComponent;
import org.bukkit.command.CommandSender;
import tc.oc.api.docs.Trophy;
import tc.oc.api.trophies.TrophyStore;
import tc.oc.api.users.UserSearchResponse;
import tc.oc.commons.bukkit.chat.Audiences;
import tc.oc.commons.bukkit.chat.Paginator;
import tc.oc.commons.bukkit.chat.PlayerComponent;
import tc.oc.commons.bukkit.chat.WarningComponent;
import tc.oc.commons.bukkit.commands.UserFinder;
import tc.oc.commons.bukkit.nick.Identity;
import tc.oc.commons.bukkit.nick.IdentityProvider;
import tc.oc.minecraft.scheduler.MainThreadExecutor;
import tc.oc.commons.core.chat.Component;
import tc.oc.commons.core.commands.CommandFutureCallback;
import tc.oc.commons.core.commands.Commands;
import tc.oc.commons.core.commands.NestedCommands;
import tc.oc.commons.core.commands.TranslatableCommandException;
public class TrophyCommands implements NestedCommands {
private final MainThreadExecutor executor;
private final TrophyStore trophyStore;
private final TrophyCase trophyCase;
private final UserFinder userFinder;
private final IdentityProvider identityProvider;
private final Audiences audiences;
@Inject TrophyCommands(MainThreadExecutor executor, TrophyStore trophyStore, TrophyCase trophyCase, UserFinder userFinder, IdentityProvider identityProvider, Audiences audiences) {
this.executor = executor;
this.trophyStore = trophyStore;
this.trophyCase = trophyCase;
this.userFinder = userFinder;
this.identityProvider = identityProvider;
this.audiences = audiences;
}
public static class Parent implements Commands {
@Command(
aliases = {"trophies", "trophy"},
desc = "Commands relating to trophies."
)
@CommandPermissions(TrophyPermissions.BASE)
@NestedCommand(value = TrophyCommands.class, executeBody = true)
public void trophies(CommandContext args, CommandSender sender) throws CommandException {}
}
@Command(
aliases = {"list"},
desc = "List the trophies of a player",
usage = "<player> <page>",
min = 0,
max = 2
)
@CommandPermissions(TrophyPermissions.LIST)
public void list(CommandContext args, CommandSender sender) throws CommandException {
final ListenableFuture<UserSearchResponse> future = userFinder.findUser(sender, args, 0, UserFinder.Default.SENDER);
executor.callback(
future,
result -> {
final Identity identity = identityProvider.createIdentity(result);
final boolean self = identity.belongsTo(sender);
executor.callback(
identity.isDisguised(sender) ? Futures.immediateFuture(ImmutableSet.of())
: trophyCase.getTrophies(result.user),
trophies -> new Paginator<Trophy>() {
@Override
protected BaseComponent title() {
return new TranslatableComponent(
self ? "trophies.list.self" : "trophies.list.other",
new PlayerComponent(identityProvider.createIdentity(result))
);
}
@Override
protected BaseComponent entry(Trophy entry, int index) {
return new Component(entry.name(), ChatColor.AQUA).extra(": ").extra(new Component(entry.description(), ChatColor.GRAY));
}
}.display(sender, trophies, args.getInteger(1, 1))
);
}
);
}
@Command(
aliases = {"grant"},
desc = "Grant a trophy to a player",
usage = "[trophy] <player>",
min = 1,
max = 2
)
@CommandPermissions(TrophyPermissions.MODIFY)
public void grant(CommandContext args, CommandSender sender) throws CommandException {
grantOrRevoke(args, sender, true);
}
@Command(
aliases = {"revoke"},
desc = "Revoke a trophy from a player",
usage = "[trophy] <player>",
min = 1,
max = 2
)
@CommandPermissions(TrophyPermissions.MODIFY)
public void revoke(CommandContext args, CommandSender sender) throws CommandException {
grantOrRevoke(args, sender, false);
}
private Trophy findTrophy(String name) throws CommandException {
return trophyStore.byName(name).orElseThrow(() -> new TranslatableCommandException("trophies.notFound", name));
}
private void grantOrRevoke(CommandContext args, CommandSender sender, boolean give) throws CommandException {
final Trophy trophy = findTrophy(args.getString(0));
executor.callback(
userFinder.findUser(sender, args, 1, UserFinder.Default.SENDER),
CommandFutureCallback.onSuccess(sender, args, result -> {
executor.callback(
trophyCase.grantOrRevoke(result.user, trophy, give),
CommandFutureCallback.onSuccess(sender, args, changed -> {
final PlayerComponent playerComponent = new PlayerComponent(identityProvider.createIdentity(result));
final Component trophyComponent = new Component(trophy.name(), ChatColor.GOLD);
audiences.get(sender).sendMessage(
changed ? new TranslatableComponent(give ? "trophies.grant.success"
: "trophies.revoke.success",
playerComponent, trophyComponent)
: new WarningComponent(give ? "trophies.grant.alreadyOwns"
: "trophies.revoke.doesNotOwn",
playerComponent, trophyComponent)
);
})
);
})
);
}
}