private void GenerateArgumentFromDefinition(Entity templateEntity, CommandDefinitionBuilder builder,
                                                    templateArgumentDefinition templateArgumentDefinition, List <char> shortNames,
                                                    string setName = null)
        {
            ArgumentBuilder argumentBuilder = builder.CreateArgument()
                                              .SetName(templateArgumentDefinition.name)
                                              .SetArgumentType(GetArgumentType(templateArgumentDefinition))
                                              .SetHelp(resolver.Resolve(
                                                           templateArgumentDefinition.help ?? string.Empty,
                                                           templateEntity))
                                              .SetRestriction(
                new ArgumentRestriction(
                    templateArgumentDefinition.ValueRestriction).Verify);

            if (templateArgumentDefinition.separatorSpecified &&
                !string.IsNullOrEmpty(templateArgumentDefinition.separator))
            {
                argumentBuilder.SetSeparator(templateArgumentDefinition.separator[0]);
            }
            if (!string.IsNullOrEmpty(templateArgumentDefinition.shortname) &&
                !shortNames.Contains(templateArgumentDefinition.shortname[0]))
            {
                argumentBuilder.SetShortName(templateArgumentDefinition.shortname[0]);
                shortNames.Add(templateArgumentDefinition.shortname[0]);
            }

            if (!string.IsNullOrEmpty(setName))
            {
                argumentBuilder.SetSetName(setName);
            }

            argumentBuilder.Build();

            ArgumentType GetArgumentType(templateArgumentDefinition argument)
            {
                return(argument.hasvalue
                           ? (argument.multiplicity == multiplicity.OneOrMore
                                  ? ArgumentType.MultipleValue
                                  : ArgumentType.SingleValue)
                           : ArgumentType.Bool);
            }
        }
        public CommandDefinition GenerateDeployCommandDefinition(Entity templateEntity, TemplateDescription currentRootTemplate,
                                                                 CommandDefinition baseCommand, ICollection <TemplateDescription> allTemplates)
        {
            if (baseCommand == null)
            {
                return(GenerateBaseCommand());
            }

            return(GenerateChildCommand());

            CommandDefinition GenerateBaseCommand()
            {
                List <char> shortNames = new List <char>();

                return(AddTemplateArguments(AddDefaultArguments(CommandDefinitionBuilder.Create()
                                                                .SetName("deploy")
                                                                .SetHelp(
                                                                    "Deploys files for production.")
                                                                .EnableUseChildVerbsAsCategory()
                                                                .AddExample($"deploy --path Path/To/Project", "Deploy files for all targets supported by project")
                                                                .AddExample($"deploy --path Path/To/Project --files doc/HelpText.txt|doc", "Deploy doc/HelpText.txt to the doc directory in the deploy directory aditonally to the normally deployed files.")
                                                                .AddExample($"deploy --path Path/To/Project --target AXCF2152 RFC4072S", "Deploy library for targets AXCF2152 and RFC4072S"),
                                                                allTemplates.Where(t => !t.isHidden & t.isRoot),
                                                                shortNames),
                                            allTemplates.Where(t => !t.isHidden & t.isRoot),
                                            shortNames, false)
                       .Build());
            }

            CommandDefinition GenerateChildCommand()
            {
                List <char> shortNames = new List <char>();

                return(AddTemplateArguments(AddDefaultArguments(CommandDefinitionBuilder.Create()
                                                                .SetBaseDefintion(baseCommand)
                                                                .SetName(currentRootTemplate
                                                                         .name)
                                                                .SetHelp(
                                                                    $"For {currentRootTemplate.name.Plural()}:"),
                                                                new[] { currentRootTemplate },
                                                                shortNames),
                                            new[] { currentRootTemplate }, shortNames, true)
                       .Build());
            }

            CommandDefinitionBuilder AddTemplateArguments(CommandDefinitionBuilder builder, IEnumerable <TemplateDescription> descriptions,
                                                          List <char> shortNames, bool addExamples)
            {
                string[] defaultArguments =
                {
                    EntityKeys.PathKey.ToUpperInvariant(),
                    Constants.OutputArgumentName.ToUpperInvariant(),
                    Constants.FilesArgumentName.ToUpperInvariant(),
                    Constants.TargetArgumentName.ToUpperInvariant(),
                };
                Dictionary <templateArgumentDefinition, string> argumentToStringDictionary = new Dictionary <templateArgumentDefinition, string>();
                List <templateArgumentDefinition> setlessArguments = new List <templateArgumentDefinition>();

                foreach (TemplateDescription description in descriptions)
                {
                    IEnumerable <templateArgumentDefinition> arguments = (description.DeployPostStep ?? Enumerable.Empty <templateDeployPostStep>())
                                                                         .SelectMany(d => d.Arguments ?? Enumerable.Empty <templateArgumentDefinition>());
                    foreach (templateArgumentDefinition argument in arguments)
                    {
                        if (!defaultArguments.Contains(argument.name.ToUpperInvariant()))
                        {
                            templateArgumentDefinition existingDefinition = argumentToStringDictionary.Keys
                                                                            .FirstOrDefault(d => d.name
                                                                                            .Equals(argument.name,
                                                                                                    StringComparison.OrdinalIgnoreCase));
                            if (existingDefinition != null)
                            {
                                if (argumentToStringDictionary[existingDefinition] != description.name)
                                {
                                    argumentToStringDictionary.Remove(existingDefinition);
                                    setlessArguments.Add(existingDefinition);
                                }
                            }
                            else
                            {
                                argumentToStringDictionary.Add(argument, description.name);
                            }
                        }
                    }

                    if (!addExamples)
                    {
                        continue;
                    }
                    IEnumerable <templateExample> examples = (description.DeployPostStep ?? Enumerable.Empty <templateDeployPostStep>())
                                                             .SelectMany(d => d.Example ?? Enumerable.Empty <templateExample>());
                    foreach (templateExample example in examples)
                    {
                        StringBuilder command = new StringBuilder("deploy");
                        ParseExample(description, example, command, false);

                        builder.AddExample(command.ToString(), example.Description ?? string.Empty);
                    }
                }

                foreach (templateArgumentDefinition argument in setlessArguments)
                {
                    GenerateArgumentFromDefinition(templateEntity, builder, argument, shortNames, null);
                }

                foreach (templateArgumentDefinition argument in argumentToStringDictionary.Keys)
                {
                    GenerateArgumentFromDefinition(templateEntity, builder, argument, shortNames, argumentToStringDictionary[argument]);
                }
                return(builder);
            }

            CommandDefinitionBuilder AddDefaultArguments(CommandDefinitionBuilder builder, IEnumerable <TemplateDescription> descriptions, List <char> shortNames)
            {
                IEnumerable <templateArgumentDefinition> arguments = descriptions.SelectMany(x => (x.DeployPostStep ?? Enumerable.Empty <templateDeployPostStep>())
                                                                                             .SelectMany(d => d.Arguments ?? Enumerable.Empty <templateArgumentDefinition>()));

                if (!ArgumentAvailableFromDescription(EntityKeys.PathKey))
                {
                    builder = builder.CreateArgument()
                              .SetName(EntityKeys.PathKey)
                              .SetShortName('p')
                              .SetHelp(
                        "The path to the project settings file or the project root directory. " +
                        "Default is the current directory.")
                              .SetArgumentType(ArgumentType.SingleValue)
                              .Build();
                    shortNames.Add('p');
                }


                if (!ArgumentAvailableFromDescription(Constants.OutputArgumentName))
                {
                    builder = builder.CreateArgument()
                              .SetName(Constants.OutputArgumentName)
                              .SetShortName('o')
                              .SetHelp("The path where the files will be deployed in. " +
                                       "Default is the 'bin/target/Release' directory. Relative paths are relative " +
                                       "to the directory defined with the '--path' option.")
                              .SetArgumentType(ArgumentType.SingleValue)
                              .Build();
                    shortNames.Add('o');
                }

                if (!ArgumentAvailableFromDescription(Constants.FilesArgumentName))
                {
                    builder = builder.CreateArgument()
                              .SetName(Constants.FilesArgumentName)
                              .SetShortName('f')
                              .SetHelp(
                        "Additional files to be deployed. Files are separated by ' '. Files need to be defined in the following format: " +
                        "path/to/source|path/to/destination(|target). The path/to/source file will be relative to the " +
                        $"root folder, defined with the '{EntityKeys.PathKey}' argument. It can contain the wildcard '*' " +
                        $"at the end of the path. In this case all files from the directory and all sub-directories will " +
                        $"be deployed. The sub-directories will be recreated in the deployment directory. The path/to/destination will be " +
                        $"relative to the output/targetname/Release directory, where output is defined with the '{Constants.OutputArgumentName}' argument. " +
                        "Optionally each file can have a target definition. Without the target definition the file is deployed to every target. " +
                        "Targets need to be defined in the following format: targetname(,targetversion). The version is optional and " +
                        "is only needed if there are multiple versions of a target in the project. The target must match one " +
                        $"of the targets of the project or one of the targets defined with the '{Constants.TargetArgumentName}' " +
                        "option, if it is defined.")
                              .SetArgumentType(ArgumentType.MultipleValue)
                              .SetSeparator(' ')
                              .Build();
                    shortNames.Add('f');
                }

                if (!ArgumentAvailableFromDescription(Constants.TargetArgumentName))
                {
                    builder = builder.CreateArgument()
                              .SetName(Constants.TargetArgumentName)
                              .SetShortName('t')
                              .SetHelp(
                        "List of targets for which files are deployed. Targets are separated by ' '. " +
                        "Targets need to be defined in the following format: targetname(,targetversion). The version is optional and " +
                        "is only needed if there are multiple versions of a target in the registered SDKs. " +
                        "If used, this option overrides the targets defined in the project. Please consider, that the actual binaries " +
                        "are deployed in the build step. Therefore if they are not built for a specific target, neither are they built here.")
                              .SetArgumentType(ArgumentType.MultipleValue)
                              .SetSeparator(' ')
                              .Build();
                    shortNames.Add('t');
                }

                if (!ArgumentAvailableFromDescription(Constants.BuildTypeArgumentName))
                {
                    builder = builder.CreateArgument()
                              .SetName(Constants.BuildTypeArgumentName)
                              .SetShortName('b')
                              .SetHelp(
                        "Build type for which the deploy should be executed. Default is 'Release'")
                              .SetArgumentType(ArgumentType.SingleValue)
                              .Build();
                    shortNames.Add('b');
                }

                return(builder);

                bool ArgumentAvailableFromDescription(string argumentName)
                {
                    templateArgumentDefinition argument = arguments.Where(a => a.name.Equals(argumentName, StringComparison.OrdinalIgnoreCase)).FirstOrDefault();

                    if (argument == null)
                    {
                        return(false);
                    }
                    GenerateArgumentFromDefinition(templateEntity, builder, argument, shortNames);
                    return(true);
                }
            }
        }
        public override Entity Resolve(Entity owner, string key, bool fallback = false)
        {
            templateRelationship[] relationships = owner.HasTemplate() ? owner.Template().Relationship : null;
            templateRelationship   relationship  = relationships?.FirstOrDefault(r => r.name.Equals(key, StringComparison.OrdinalIgnoreCase))
                                                   ?? relationships?.FirstOrDefault(r => r.name.Equals(key.Singular(), StringComparison.OrdinalIgnoreCase));
            TemplateDescription relationshipDescription = relationship != null?templateRepository.Template(relationship.type) : null;

            if (relationshipDescription != null)
            {
                return(GetRelationship());
            }
            if (key == EntityKeys.PathKey)
            {
                return(GetCommandPath());
            }
            if (key == EntityKeys.FullNameKey)
            {
                return(GetFullName());
            }

            return(GetArgument());

            Entity GetFullName()
            {
                SingleValueArgument nameArgument = owner.Value <CommandDefinition>()
                                                   .Argument <SingleValueArgument>(EntityKeys.NameKey);
                SingleValueArgument namespaceArgument = owner.Value <CommandDefinition>()
                                                        .Argument <SingleValueArgument>(EntityKeys.NamespaceKey);
                string fullName = codeLanguage.CombineNamespace(Value(namespaceArgument, EntityKeys.NamespaceKey),
                                                                Value(nameArgument, EntityKeys.NameKey));

                return(owner.Create(key, fullName, nameArgument, namespaceArgument));
            }

            Entity GetCommandPath()
            {
                SingleValueArgument singleValueArgument = owner.Value <CommandDefinition>()
                                                          .Argument <SingleValueArgument>(EntityKeys.OutputKey);
                string basePath = owner.IsRoot() ? string.Empty : owner.Root.Path;
                string path     = fileSystem.GetDirectory(Value(singleValueArgument), basePath, false).FullName;

                return(owner.Create(key, path, singleValueArgument));
            }

            string Value(Argument arg, string argumentName = null)
            {
                if (string.IsNullOrEmpty(argumentName))
                {
                    argumentName = key;
                }
                if (arg != null && arg is BoolArgument boolArgument)
                {
                    return(boolArgument.Value.ToString(CultureInfo.InvariantCulture));
                }

                SingleValueArgument singleValueArgument = (SingleValueArgument)arg;

                if (singleValueArgument?.Value != null)
                {
                    return(ResolveValue(singleValueArgument.Value));
                }

                if ((arg != null && arg.Name != argumentName && TryGetTemplateDefault(arg.Name, out string result)) ||
                    TryGetTemplateDefault(argumentName, out result))
                {
                    return(result);
                }

                return(string.Empty);
            }

            string ResolveValue(string value)
            {
                string result = templateResolver.Resolve(value, owner);

                if (TryGetTemplateFormat(out string format) &&
                    !string.IsNullOrEmpty(format))
                {
                    Entity temporary = owner.Create(key, result);
                    result = temporary.Format()[format].Value <string>();
                }
                return(result);

                bool TryGetTemplateFormat(out string formattedValue)
                {
                    TemplateDescription description = owner.HasTemplate() ? owner.Template() : null;

                    if (description != null)
                    {
                        templateArgumentDefinition templateArgument = description.Arguments.FirstOrDefault(a => a.name == key);
                        if (templateArgument != null)
                        {
                            formattedValue = templateArgument.format;
                            return(true);
                        }
                    }

                    formattedValue = null;
                    return(false);
                }
            }

            bool TryGetTemplateDefault(string argumentName, out string value)
            {
                TemplateDescription description = owner.HasTemplate() ? owner.Template() : null;

                if (description != null)
                {
                    templateArgumentDefinition templateArgument = description.Arguments.FirstOrDefault(a => a.name == argumentName);
                    if (templateArgument != null)
                    {
                        value = ResolveValue(templateArgument.@default);
                        return(true);
                    }
                }

                value = null;
                return(false);
            }

            Entity GetArgument()
            {
                Argument argument = owner.Value <CommandDefinition>().Argument <Argument>(key);

                if (argument == null)
                {
                    if (TryGetTemplateDefault(key, out string value))
                    {
                        return(owner.Create(key, value));
                    }
                    throw new ContentProviderException(key, owner);
                }

                switch (argument)
                {
                case BoolArgument boolArgument:
                    return(owner.Create(key, Value(argument), boolArgument));

                case SingleValueArgument singleValueArgument:
                    return(owner.Create(key, Value(argument), singleValueArgument));

                case MultipleValueArgument multipleValueArgument:
                    IEnumerable <Entity> values = Values(multipleValueArgument);
                    return(owner.Create(key, values));

                default:
                    throw new InvalidOperationException($"Unkown argument type {argument.GetType()}");
                }

                IEnumerable <Entity> Values(MultipleValueArgument multipleValueArgument)
                {
                    if (multipleValueArgument.Values != null)
                    {
                        return(multipleValueArgument.Values
                               .Select(s => owner.Create(key,
                                                         ResolveValue(s),
                                                         multipleValueArgument)));
                    }

                    if (TryGetTemplateDefault(key, out string result))
                    {
                        return(new[] { owner.Create(key, result, multipleValueArgument) });
                    }

                    throw new ContentProviderException(key, owner);
                }
            }

            Entity GetRelationship()
            {
                if (relationshipDescription.isRoot)
                {
                    return(owner.Root);
                }

                if (relationship.multiplicity == multiplicity.One)
                {
                    string name = owner.Value <CommandDefinition>()
                                  .Argument <SingleValueArgument>(relationship.name)
                                  .Value;
                    return(relationship.GetRelationship(relationshipDescription, owner, name));
                }

                IEnumerable <string> names = owner.Value <CommandDefinition>()
                                             .Argument <MultipleValueArgument>(relationship.name)
                                             .Values;

                return(relationship.GetRelationship(relationshipDescription, owner, names.ToArray()));
            }
        }