/// <summary>
        /// Extract option accepted values from object attributes.
        /// </summary>
        /// <param name="prop">Property object</param>
        /// <param name="optionSpec">Option specification object</param>
        private void ExtractOptionValuesOfAttributes(PropertyInfo prop, ProgramOptionBase optionSpec)
        {
            /// <remarks>
            /// <see cref="ProgramOptionValueAttribute"/> is <see cref="ProgramOptionValuesAttribute"/>. Therefore, listing <see cref="ProgramOptionValueAttribute"/> already included.
            /// </remarks>
            IEnumerable<Attribute> valuesAttrList = prop.GetCustomAttributes(typeof(ProgramOptionValuesAttribute));

            if (valuesAttrList == null) return;

            valuesAttrList.ToList().ForEach(v =>
            {
                IEnumerable<ProgramAcceptedValue> acceptedValues = (v as ProgramOptionValuesAttribute).Values.Select(s =>
                {
                    string[] values = s.Split('|');

                    if (values.Length < 1) return null;

                    var value = new ProgramAcceptedValue
                    {
                        Value = values.First().Trim()
                    };

                    if (values.Length > 1)
                    {
                        value.Description = values.Skip(1).First().Trim();
                    }

                    return value;

                }).Where(w => w != null);

                optionSpec.AcceptedValues.AddRange(acceptedValues);
            });
        }
        /// <summary>
        /// Populate object options attributes
        /// </summary>
        private void PopulateOptionsAttributes()
        {
            foreach (var prop in GetType().GetProperties())
            {
                var optionAttr = prop.GetCustomAttribute(typeof(OptionAttribute)) as OptionAttribute;

                if (optionAttr != null)
                {
                    ProgramOptionBase option = Spec.GetOptionByName(optionAttr.Name);

                    if (option == null)
                    {
                        continue;
                    }

                    object optionValue = option.ConvertValueToType(prop.PropertyType);

                    if (optionValue != null)
                    {
                        prop.SetValue(this, optionValue);
                    }
                }
            }
        }
        /// <summary>
        /// Read flags and options values from arguments
        /// </summary>
        /// <param name="args">The arguments</param>
        private void ReadFlagsOptions(string[] args)
        {
            var remainingArgs = new List<string>();
            int maxArgIdx = args.Length - 1;

            for (int argc = 0; argc <= maxArgIdx; argc++)
            {
                string argOriginal = args[argc];
                string arg = argOriginal;

                // Minimal pattern: "-c"
                if (arg.Length < 2)
                {
                    remainingArgs.Add(arg);
                    continue;
                }

                char first = arg[0];
                char second = arg[1];
                bool isFullPattern = false;
                bool isShortPattern = false;

                // Full name pattern: "--full-name
                if (first == '-' && second == '-')
                {
                    arg = new string(arg.Skip(2).ToArray());
                    isFullPattern = true;
                }

                // Short name pattern: "-name"
                else if (first == '-')
                {
                    arg = new string(arg.Skip(1).ToArray());
                    isShortPattern = true;
                }

                // Invalid pattern: "?"
                if (!isFullPattern && !isShortPattern)
                {
                    remainingArgs.Add(argOriginal);
                    continue;
                }

                string argValue = null;

                // Pattern: "-x=Value", "--xx=Value"
                if (arg.Contains('='))
                {
                    var eqIdx = arg.IndexOf('=');

                    argValue = arg.Substring(eqIdx + 1);
                    arg = arg.Substring(0, eqIdx);
                }

                var argTokens = new List<string>();

                // The word is the token itself
                if (isFullPattern)
                {
                    argTokens.Add(arg);
                }

                // Each character is a token
                else if (isShortPattern)
                {
                    arg
                        .ToCharArray()
                        .ToList()
                        .ForEach(c => argTokens.Add(c.ToString()));
                }

                argTokens.ForEach(token =>
                {
                    ProgramOptionBase option = Spec.GetOptionByToken(token);

                    // Option not found!
                    if (option == null)
                    {
                        switch (UnknownOptionAction)
                        {
                            case UnknownOptionAction.ThrowException:
                                throw new UnknownOptionException(token, isFullPattern);

                            case UnknownOptionAction.ByPass:
                                if (isFullPattern)
                                {
                                    remainingArgs.Add(argOriginal);
                                }
                                else if (argValue != null)
                                {
                                    remainingArgs.Add(string.Format("-{0}={1}", token, argValue));
                                }
                                else
                                {
                                    remainingArgs.Add(string.Format("-{0}", token));
                                }
                                return; // argTokens.ForEach

                            case UnknownOptionAction.Discard:
                            default:
                                return; // argTokens.ForEach
                        }
                    }

                    // Repeated option!
                    else if (option.HasValue)
                    {
                        switch (RepeatedOptionAction)
                        {
                            case RepeatedOptionAction.ThrowException:
                                throw new RepeatedOptionException(token, isFullPattern);

                            case RepeatedOptionAction.Replace:
                                // TODO: ???
                                // Se não existe @argValue, o valor é o próximo argumento
                                // > Se não existe o próximo argumento, lança exceção de opção inválida?
                                //   - aqui podemos ou lançar a exceção,
                                //   - ou incluir uma flag que diz se é pra lanaçar a exceção
                                //   - ou simplesmente não atribuir o valor da opção
                                if (!option.IsFlag && argValue == null && argc < maxArgIdx)
                                {
                                    int nextArgIdx = GetNextValueParameter(argc + 1, args, out argValue);

                                    // Found!
                                    if (nextArgIdx != argc)
                                    {
                                        // Removes argument  found, and reconfigures context
                                        var argList = args.ToList();

                                        argList.RemoveAt(nextArgIdx);

                                        args = argList.ToArray();
                                        maxArgIdx--;
                                    }
                                }

                                if (!option.IsFlag && argValue != null)
                                {
                                    option.InformedValue = argValue;
                                }
                                return; // argTokens.ForEach

                            case RepeatedOptionAction.Ignore:
                            default:
                                return; // argTokens.ForEach
                        }
                    }

                    // Flag found for the first time
                    else if (option.IsFlag)
                    {
                        option.HasValue = true;
                        option.InformedValue = bool.TrueString;
                    }

                    // Option found for the first time
                    else
                    {
                        // TODO: ???
                        // Se não existe @argValue, o valor é o próximo argumento
                        // > Se não existe o próximo argumento, lança exceção de opção inválida?
                        //   - aqui podemos ou lançar a exceção,
                        //   - ou incluir uma flag que diz se é pra lanaçar a exceção
                        //   - ou simplesmente não atribuir o valor da opção
                        if (argValue == null && argc < maxArgIdx)
                        {
                            int nextArgIdx = GetNextValueParameter(argc + 1, args, out argValue);

                            // Found!
                            if (nextArgIdx != argc)
                            {
                                // Removes argument  found, and reconfigures context
                                var argList = args.ToList();

                                argList.RemoveAt(nextArgIdx);

                                args = argList.ToArray();
                                maxArgIdx--;
                            }
                        }

                        if (argValue != null)
                        {
                            option.HasValue = true;
                            option.InformedValue = argValue;
                        }
                    }
                });
            }

            Arguments = remainingArgs.ToArray();
        }
        private void AddSpecification(string name, ProgramOptionBase option)
        {
            if (string.IsNullOrWhiteSpace(name))
            {
                throw new ArgumentNullException(nameof(name));
            }

            // TODO: Patter is required?
            if (string.IsNullOrWhiteSpace(option.Pattern))
            {
                throw new ArgumentNullException(nameof(option.Pattern).ToLower());
            }

            // TODO: Description is required?
            if (string.IsNullOrWhiteSpace(option.Description))
            {
                throw new ArgumentNullException(nameof(option.Description).ToLower());
            }

            // Duplicate key exception by Dictionary
            _specByName.Add(name, option);

            // Duplicate key exception by Dictionary
            option.Pattern.Split('|').ToList().ForEach(keyword =>
            {
                var argumentException = new ArgumentException(string.Format("Invalid keyword pattern: {0}", keyword));

                // Minimal pattern: "-c"
                if (keyword.Length < 2)
                {
                    throw argumentException;
                }

                char first         = keyword[0];
                char second        = keyword[1];
                bool isFullPattern = true;

                // Full name pattern: "--full-name
                if (first == '-' && second == '-')
                {
                    keyword = new string(keyword.Skip(2).ToArray());
                }

                // Short name pattern: "-name"
                else if (first == '-')
                {
                    keyword       = new string(keyword.Skip(1).ToArray());
                    isFullPattern = false;
                }

                // Invalid pattern: "?"
                else
                {
                    throw argumentException;
                }

                // Invalid pattern: "--", "-\s", "- "
                if (string.IsNullOrWhiteSpace(keyword))
                {
                    throw argumentException;
                }

                if (isFullPattern)
                {
                    _specByToken.Add(keyword, option);
                }
                else
                {
                    keyword
                    .ToCharArray()
                    .ToList()
                    .ForEach(c => _specByToken.Add(c.ToString(), option));
                }
            });
        }