package org.mafagafogigante.dungeon.commands;
import org.mafagafogigante.dungeon.game.DungeonString;
import org.mafagafogigante.dungeon.io.Writer;
import org.mafagafogigante.dungeon.logging.DungeonLogger;
import org.mafagafogigante.dungeon.util.Utils;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* A set of Commands.
*/
public final class CommandSet {
private static final int COMMAND_NAME_COLUMN_WIDTH = 20;
private final List<Command> commands = new ArrayList<>();
private final List<CommandDescription> commandDescriptions = new ArrayList<>();
private CommandSet() {
}
/**
* Constructs an empty CommandSet containing only the "commands" Command.
*/
static CommandSet emptyCommandSet() {
final CommandSet commandSet = new CommandSet();
commandSet.addCommand(new Command("commands", "Lists all commands in this command set.") {
@Override
public void execute(@NotNull String[] arguments) {
String filter = arguments.length == 0 ? null : arguments[0];
List<CommandDescription> descriptions = commandSet.getCommandDescriptions();
DungeonString dungeonString = new DungeonString();
int count = 0;
for (CommandDescription description : descriptions) {
if (filter == null || Utils.startsWithIgnoreCase(description.getName(), filter)) {
count++;
dungeonString.append(StringUtils.rightPad(description.getName(), COMMAND_NAME_COLUMN_WIDTH));
dungeonString.append(description.getInfo());
dungeonString.append("\n");
}
}
if (count == 0 && filter != null) {
Writer.write("No command starts with '" + filter + "'.");
} else {
if (count > 1) {
dungeonString.append("\nListed ");
dungeonString.append(String.valueOf(count));
dungeonString.append(" commands.");
if (filter == null) {
dungeonString.append("\nYou can filter the output of this command by typing the beginning of a command.");
}
}
Writer.write(dungeonString);
}
}
});
return commandSet;
}
/**
* Retrieves a Command corresponding to the specified token or null if no command matches the token.
*/
Command getCommand(String token) {
for (Command command : commands) {
if (command.getDescription().getName().equalsIgnoreCase(token)) {
return command;
}
}
return null;
}
/**
* Adds a Command to this CommandSet.
*
* @param command a Command object, not null
*/
void addCommand(Command command) {
if (command == null) {
DungeonLogger.warning("Passed null to CommandSet.addCommand().");
} else if (commands.contains(command)) {
DungeonLogger.warning("Attempted to add the same Command to a CommandSet twice.");
} else {
commands.add(command);
commandDescriptions.add(command.getDescription());
}
}
/**
* Returns an unmodifiable view of the List of CommandDescriptions.
*
* @return an unmodifiable List of CommandDescriptions
*/
private List<CommandDescription> getCommandDescriptions() {
return Collections.unmodifiableList(commandDescriptions);
}
@Override
public String toString() {
return String.format("CommandSet of size %d.", commands.size());
}
/**
* Retrieves a list of the names of the commands closest to the provided token according to their Levenshtein
* distance.
*/
List<String> getClosestCommands(final String token) {
List<String> closestCommands = new ArrayList<>();
int best = Integer.MAX_VALUE;
for (Command command : commands) {
String commandName = command.getDescription().getName();
int distance = StringDistanceMetrics.levenshteinDistance(token, commandName);
if (distance < best) {
closestCommands.clear();
best = distance;
}
if (distance == best) {
closestCommands.add(commandName);
}
}
return closestCommands;
}
}