/**
   * Attempt to execute a command. This version takes a separate command name (for the root command)
   * and then a list of following arguments.
   *
   * @param cmd command to run
   * @param args arguments
   * @param player command source
   * @param methodArgs method arguments
   * @throws CommandException
   */
  public void execute(String cmd, String[] args, T player, Object... methodArgs)
      throws CommandException {

    String[] newArgs = new String[args.length + 1];
    System.arraycopy(args, 0, newArgs, 1, args.length);
    newArgs[0] = cmd;
    Object[] newMethodArgs = new Object[methodArgs.length + 1];
    System.arraycopy(methodArgs, 0, newMethodArgs, 1, methodArgs.length);

    executeMethod(null, newArgs, player, newMethodArgs, 0);
  }
  /**
   * Attempt to execute a command.
   *
   * @param parent
   * @param args
   * @param player
   * @param methodArgs
   * @param level
   * @throws CommandException
   */
  public void executeMethod(Method parent, String[] args, T player, Object[] methodArgs, int level)
      throws CommandException {

    String cmdName = args[level];

    Map<String, Method> map = commands.get(parent);
    Method method = map.get(cmdName.toLowerCase());

    if (method == null) {
      if (parent == null) { // Root
        throw new UnhandledCommandException();
      } else {
        throw new MissingNestedCommandException(
            "Unknown command: " + cmdName, getNestedUsage(args, level - 1, parent, player));
      }
    }

    checkPermission(player, method);

    int argsCount = args.length - 1 - level;

    // checks if we need to execute the body of the nested command method (false)
    // or display the help what commands are available (true)
    // this is all for an args count of 0 if it is > 0 and a NestedCommand Annotation is present
    // it will always handle the methods that NestedCommand points to
    // e.g.:
    //  - /cmd - @NestedCommand(executeBody = true) will go into the else loop and execute code in
    // that method
    //  - /cmd <arg1> <arg2> - @NestedCommand(executeBody = true) will always go to the nested
    // command class
    //  - /cmd <arg1> - @NestedCommand(executeBody = false) will always go to the nested command
    // class not matter the args
    boolean executeNested =
        method.isAnnotationPresent(NestedCommand.class)
            && (argsCount > 0 || !method.getAnnotation(NestedCommand.class).executeBody());

    if (executeNested) {
      if (argsCount == 0) {
        throw new MissingNestedCommandException(
            "Sub-command required.", getNestedUsage(args, level, method, player));
      } else {
        executeMethod(method, args, player, methodArgs, level + 1);
      }
    } else if (method.isAnnotationPresent(CommandAlias.class)) {
      CommandAlias aCmd = method.getAnnotation(CommandAlias.class);
      executeMethod(parent, aCmd.value(), player, methodArgs, level);
    } else {
      if (method.isAnnotationPresent(RequireSenderType.class)) {
        WrappedCommandSender.Type requiredType =
            method.getAnnotation(RequireSenderType.class).value();

        if (!getType(player).equals(requiredType)) {
          throw new CommandUsageException(
              "Wrong sender type.",
              String.format(
                  "You need to be a %s to use this command!", requiredType.name().toLowerCase()));
        }
      }

      Command cmd = method.getAnnotation(Command.class);

      String[] newArgs = new String[args.length - level];
      System.arraycopy(args, level, newArgs, 0, args.length - level);

      final Set<Character> valueFlags = new HashSet<Character>();

      char[] flags = cmd.flags().toCharArray();
      Set<Character> newFlags = new HashSet<Character>();
      for (int i = 0; i < flags.length; ++i) {
        if (flags.length > i + 1 && flags[i + 1] == ':') {
          valueFlags.add(flags[i]);
          ++i;
        }
        newFlags.add(flags[i]);
      }

      CommandContext context = new CommandContext(newArgs, valueFlags);

      if (context.argsLength() < cmd.min()) {
        throw new CommandUsageException("Too few arguments.", getUsage(args, level, cmd));
      }

      if (cmd.max() != -1 && context.argsLength() > cmd.max()) {
        throw new CommandUsageException("Too many arguments.", getUsage(args, level, cmd));
      }

      if (!cmd.anyFlags()) {
        for (char flag : context.getFlags()) {
          if (!newFlags.contains(flag)) {
            throw new CommandUsageException("Unknown flag: " + flag, getUsage(args, level, cmd));
          }
        }
      }

      methodArgs[0] = context;

      Object instance = instances.get(method);

      invokeMethod(parent, args, player, method, instance, methodArgs, argsCount);
    }
  }