package fi.helsinki.cs.tmc.cli.command;

import fi.helsinki.cs.tmc.cli.Application;
import fi.helsinki.cs.tmc.cli.core.AbstractCommand;
import fi.helsinki.cs.tmc.cli.core.CliContext;
import fi.helsinki.cs.tmc.cli.core.Command;
import fi.helsinki.cs.tmc.cli.core.CommandFactory;
import fi.helsinki.cs.tmc.cli.io.Io;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.Options;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;

@Command(name = "help", desc = "List every command")
public class HelpCommand extends AbstractCommand {
    private int longestNameLength;
    private Io io;

    @Override
    public void getOptions(Options options) {}

    @Override
    public void run(CliContext context, CommandLine args) {
        Application app = context.getApp();
        this.io = context.getIo();

        StringBuilder sb = new StringBuilder();
        sb.append("TMC commands:\n");

        List<String> commandStrings = getCommandStrings();
        Collections.sort(commandStrings);
        for (String commandString : commandStrings) {
            sb.append(commandString).append("\n");
        }

        app.printHelp(sb.toString());
    }

    private List<String> getCommandStrings() {
        List<String> strings = new ArrayList<>();
        List<Class<Command>> commands = CommandFactory.getCommands();

        longestNameLength = longestName(commands);
        for (Class<Command> commandClass : commands) {
            Command command = CommandFactory.getCommand(commandClass);
            strings.add(createCommandString(command));
        }
        return strings;
    }

    @SuppressWarnings("unchecked")
    private Class<Command> castToCommandClass(Class klass) {
        return (Class<Command>) klass;
    }

    private String createCommandString(Command command) {
        StringBuilder builder = new StringBuilder();
        builder.append("  ").append(command.name());
        for (int i = 0; i < longestNameLength - command.name().length() + 1; i++) {
            builder.append(" ");
        }
        builder.append(command.desc());
        return builder.toString();
    }

    private int longestName(List<Class<Command>> commandList) {
        int longest = 0;
        for (Class<Command> commandClass : commandList) {
            Command command = CommandFactory.getCommand(commandClass);
            longest = Math.max(longest, command.name().length());
        }
        return longest;
    }
}

@@ -600,16 +600,17 @@
 mmand %7B%0A
+%0A
     priv
@@ -652,14 +652,145 @@
 ate 
-Io io;
+CliContext context;%0A    private Io io;%0A%0A    @Override%0A    public String%5B%5D getUsages() %7B%0A        return new String%5B%5D %7B%22%5Bcategory%5D%22%7D;%0A    %7D
 %0A%0A  
@@ -963,24 +963,56 @@
 t.getApp();%0A
+        this.context = context;%0A
         this
@@ -1053,79 +1053,326 @@
 ring
-Builder sb = new StringBuilder();%0A        sb.append(%22TMC commands:%5Cn%22);
+ category = handleArgs(args);%0A        if (category == null) %7B%0A            return;%0A        %7D%0A%0A        StringBuilder sb = new StringBuilder();%0A        if (category.equals(%22%22)) %7B%0A            sb.append(%22TMC commands:%5Cn%22);%0A        %7D else %7B%0A            sb.append(%22TMC commands in %22).append(category).append(%22:%5Cn%22);%0A        %7D
 %0A%0A  
@@ -1425,16 +1425,24 @@
 Strings(
+category
 );%0A     
@@ -1631,32 +1631,759 @@
 ring());%0A    %7D%0A%0A
+    private String handleArgs(CommandLine args) %7B%0A        String%5B%5D stringArguments = args.getArgs();%0A        if (stringArguments.length %3E 1) %7B%0A            io.errorln(%22Too many arguments.%22);%0A            printUsage(context);%0A            return null;%0A        %7D%0A        String category = %22%22;%0A        if (stringArguments.length == 1) %7B%0A            category = stringArguments%5B0%5D;%0A        %7D%0A        if (category.equals(%22all%22)) %7B%0A            return category;%0A        %7D%0A        Set%3CString%3E helpCategories = CommandFactory.getCommandCategories();%0A        if(!helpCategories.contains(category)) %7B%0A            io.errorln(%22Unknown command category %5C%22%22 + category + %22%5C%22.%22);%0A            return null;%0A        %7D%0A        return category;%0A    %7D%0A%0A
     private List
@@ -2409,16 +2409,31 @@
 Strings(
+String category
 ) %7B%0A    
@@ -2519,40 +2519,196 @@
 ands
- = CommandFactory.getCommands();
+;%0A        if (category.equals(%22all%22)) %7B%0A            commands = CommandFactory.getCommands();%0A        %7D else %7B%0A            commands = CommandFactory.getCategoryCommands(category);%0A        %7D
 %0A%0A  
@@ -2939,32 +2939,92 @@
 nd));%0A        %7D%0A
+        longestNameLength = Math.max(longestNameLength, 8);%0A
         return s

Support command categories in help command.