content: /*
   * Copyright (c) 2016-2018 Daniel Ennis (Aikar) - MIT License
   *
   *  Permission is hereby granted, free of charge, to any person obtaining
   *  a copy of this software and associated documentation files (the
   *  "Software"), to deal in the Software without restriction, including
   *  without limitation the rights to use, copy, modify, merge, publish,
   *  distribute, sublicense, and/or sell copies of the Software, and to
   *  permit persons to whom the Software is furnished to do so, subject to
   *  the following conditions:
   *
   *  The above copyright notice and this permission notice shall be
   *  included in all copies or substantial portions of the Software.
   *
   *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
   *  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
   *  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
   *  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
   *  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
   *  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
   *  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
   */
  
  package co.aikar.commands;
  
  import co.aikar.commands.annotation.CommandPermission;
  import co.aikar.commands.annotation.Conditions;
  import co.aikar.commands.annotation.ConsumesRest;
  import co.aikar.commands.annotation.Default;
  import co.aikar.commands.annotation.Description;
  import co.aikar.commands.annotation.Flags;
  import co.aikar.commands.annotation.Name;
  import co.aikar.commands.annotation.Optional;
  import co.aikar.commands.annotation.Single;
  import co.aikar.commands.annotation.Syntax;
  import co.aikar.commands.annotation.Values;
  import co.aikar.commands.contexts.ContextResolver;
  import co.aikar.commands.contexts.IssuerAwareContextResolver;
  import co.aikar.commands.contexts.IssuerOnlyContextResolver;
  import co.aikar.commands.contexts.OptionalContextResolver;
  import co.aikar.locales.MessageKey;
  
  import java.lang.reflect.Parameter;
  import java.util.Arrays;
  import java.util.HashMap;
  import java.util.HashSet;
  import java.util.Map;
  import java.util.Set;
  
  public class CommandParameter<CEC extends CommandExecutionContext<CEC, ? extends CommandIssuer>> {
      private final Parameter parameter;
      private final Class<?> type;
      private final String name;
      private final CommandManager manager;
      private final int paramIndex;
  
      private ContextResolver<?, CEC> resolver;
      private boolean optional;
      private Set<String> permissions = new HashSet<>();
      private String permission;
      private String description;
      private String defaultValue;
      private String syntax;
      private String conditions;
      private boolean requiresInput;
      private boolean commandIssuer;
      private String[] values;
      private Map<String, String> flags;
      private boolean canConsumeInput;
      private boolean optionalResolver;
      boolean consumesRest;
      private boolean isLast;
      private boolean isOptionalInput;
      private CommandParameter<CEC> nextParam;
  
      public CommandParameter(RegisteredCommand<CEC> command, Parameter param, int paramIndex, boolean isLast) {
          this.parameter = param;
          this.isLast = isLast;
          this.type = param.getType();
          this.manager = command.manager;
          this.paramIndex = paramIndex;
          Annotations annotations = manager.getAnnotations();
  
          String annotationName = annotations.getAnnotationValue(param, Name.class, Annotations.REPLACEMENTS);
          this.name = annotationName != null ? annotationName : param.getName();
          this.defaultValue = annotations.getAnnotationValue(param, Default.class, Annotations.REPLACEMENTS | (type != String.class ? Annotations.NO_EMPTY : 0));
          this.description = annotations.getAnnotationValue(param, Description.class, Annotations.REPLACEMENTS | Annotations.DEFAULT_EMPTY);
          this.conditions = annotations.getAnnotationValue(param, Conditions.class, Annotations.REPLACEMENTS | Annotations.NO_EMPTY);
  
          //noinspection unchecked
          this.resolver = manager.getCommandContexts().getResolver(type);
          if (this.resolver == null) {
              ACFUtil.sneaky(new InvalidCommandContextException(
                      "Parameter " + type.getSimpleName() + " of " + command + " has no applicable context resolver"
              ));
          }
  
          this.optional = annotations.hasAnnotation(param, Optional.class) || this.defaultValue != null || (isLast && type == String[].class);
          this.permission = annotations.getAnnotationValue(param, CommandPermission.class, Annotations.REPLACEMENTS | Annotations.NO_EMPTY);
          this.optionalResolver = isOptionalResolver(resolver);
          this.requiresInput = !this.optional && !this.optionalResolver;
          //noinspection unchecked
          this.commandIssuer = paramIndex == 0 && manager.isCommandIssuer(type);
          this.canConsumeInput = !this.commandIssuer && !(resolver instanceof IssuerOnlyContextResolver);
          this.consumesRest = isLast && (annotations.hasAnnotation(param, ConsumesRest.class)
                  || (type == String.class && !annotations.hasAnnotation(param, Single.class))
                  || (type == String[].class));
  
          this.values = annotations.getAnnotationValues(param, Values.class, Annotations.REPLACEMENTS | Annotations.NO_EMPTY);
  
          this.syntax = null;
          this.isOptionalInput = !requiresInput && canConsumeInput;
  
          if (!commandIssuer) {
              this.syntax = annotations.getAnnotationValue(param, Syntax.class);
          }
  
          this.flags = new HashMap<>();
          String flags = annotations.getAnnotationValue(param, Flags.class, Annotations.REPLACEMENTS | Annotations.NO_EMPTY);
          if (flags != null) {
              parseFlags(flags);
          }
          inheritContextFlags(command.scope);
          this.computePermissions();
      }
  
      private void inheritContextFlags(BaseCommand scope) {
          if (!scope.contextFlags.isEmpty()) {
              Class<?> pCls = this.type;
              do {
                  parseFlags(scope.contextFlags.get(pCls));
              } while ((pCls = pCls.getSuperclass()) != null);
          }
          if (scope.parentCommand != null) {
              inheritContextFlags(scope.parentCommand);
          }
      }
  
      private void parseFlags(String flags) {
          if (flags != null) {
              for (String s : ACFPatterns.COMMA.split(manager.getCommandReplacements().replace(flags))) {
                  String[] v = ACFPatterns.EQUALS.split(s, 2);
                  if (!this.flags.containsKey(v[0])) {
                      this.flags.put(v[0], v.length > 1 ? v[1] : null);
                  }
              }
          }
      }
  
      private void computePermissions() {
          this.permissions.clear();
          if (this.permission != null && !this.permission.isEmpty()) {
              this.permissions.addAll(Arrays.asList(ACFPatterns.COMMA.split(this.permission)));
          }
      }
  
      private boolean isOptionalResolver(ContextResolver<?, CEC> resolver) {
          return resolver instanceof IssuerAwareContextResolver
                  || resolver instanceof IssuerOnlyContextResolver
                  || resolver instanceof OptionalContextResolver;
      }
  
  
      public Parameter getParameter() {
          return parameter;
      }
  
      public Class<?> getType() {
          return type;
      }
  
      public String getName() {
          return name;
      }
  
      public String getDisplayName(CommandIssuer issuer) {
          String translated = manager.getLocales().getOptionalMessage(issuer, MessageKey.of("acf-core.parameter." + name.toLowerCase()));
          return translated != null ? translated : name;
      }
  
      public CommandManager getManager() {
          return manager;
      }
  
      public int getParamIndex() {
          return paramIndex;
      }
  
      public ContextResolver<?, CEC> getResolver() {
          return resolver;
      }
  
      public void setResolver(ContextResolver<?, CEC> resolver) {
          this.resolver = resolver;
      }
  
      public boolean isOptionalInput() {
          return isOptionalInput;
      }
  
      public boolean isOptional() {
          return optional;
      }
  
      public void setOptional(boolean optional) {
          this.optional = optional;
      }
  
      public String getDescription() {
          return description;
      }
  
      public void setDescription(String description) {
          this.description = description;
      }
  
      public String getDefaultValue() {
          return defaultValue;
      }
  
      public void setDefaultValue(String defaultValue) {
          this.defaultValue = defaultValue;
      }
  
      public boolean isCommandIssuer() {
          return commandIssuer;
      }
  
      public void setCommandIssuer(boolean commandIssuer) {
          this.commandIssuer = commandIssuer;
      }
  
      public String[] getValues() {
          return values;
      }
  
      public void setValues(String[] values) {
          this.values = values;
      }
  
      public Map<String, String> getFlags() {
          return flags;
      }
  
      public void setFlags(Map<String, String> flags) {
          this.flags = flags;
      }
  
      public boolean canConsumeInput() {
          return canConsumeInput;
      }
  
      public void setCanConsumeInput(boolean canConsumeInput) {
          this.canConsumeInput = canConsumeInput;
      }
  
      public void setOptionalResolver(boolean optionalResolver) {
          this.optionalResolver = optionalResolver;
      }
  
      public boolean isOptionalResolver() {
          return optionalResolver;
      }
  
      public boolean requiresInput() {
          return requiresInput;
      }
  
      public void setRequiresInput(boolean requiresInput) {
          this.requiresInput = requiresInput;
      }
  
      public String getSyntax() {
          return getSyntax(null);
      }
  
      public String getSyntax(CommandIssuer issuer) {
          if (commandIssuer) return null;
          if (syntax == null) {
              if (isOptionalInput) {
                  return "[" + getDisplayName(issuer) + "]";
              } else if (requiresInput) {
                  return "<" + getDisplayName(issuer) + ">";
              }
          }
          return syntax;
      }
  
      public void setSyntax(String syntax) {
          this.syntax = syntax;
      }
  
      public String getConditions() {
          return conditions;
      }
  
      public void setConditions(String conditions) {
          this.conditions = conditions;
      }
  
      public Set<String> getRequiredPermissions() {
          return permissions;
      }
  
      public void setNextParam(CommandParameter<CEC> nextParam) {
          this.nextParam = nextParam;
      }
  
      public CommandParameter<CEC> getNextParam() {
          return nextParam;
      }
  
      public boolean canExecuteWithoutInput() {
          return (!canConsumeInput || isOptionalInput()) && (nextParam == null || nextParam.canExecuteWithoutInput());
      }
  
      public boolean isLast() {
          return isLast;
      }
  }
  