/// <summary>
 ///     Construtor.
 /// </summary>
 /// <param name="securables">Lista de todos os itens do sistema que tem permissão associada.</param>
 /// <param name="permissions">Lista de todas as permissões possíveis.</param>
 /// <param name="charset">Modos possíveis do texto puro que representa o mapa de permissões.</param>
 public PermissionMap(
     IEnumerable <TSecurable> securables,
     IEnumerable <TPermission> permissions,
     PermissionMapCharset charset) :
     this(securables, permissions, charset, null)
 {
 }
        /// <summary>
        ///     Construtor privado.
        /// </summary>
        /// <param name="securables">Lista de todos os itens do sistema que tem permissão associada.</param>
        /// <param name="permissions">Lista de todas as permissões possíveis.</param>
        /// <param name="charset">Modos possíveis do texto puro que representa o mapa de permissões.</param>
        /// <param name="charsetValue">Texto customizado para o mapa de permissões.</param>
        private PermissionMap(
            IEnumerable <TSecurable> securables,
            IEnumerable <TPermission> permissions,
            PermissionMapCharset charset,
            string?charsetValue)
        {
            Permissions = permissions
                          .OrderBy(a => $"{a}")
                          .ToArray();

            Securables = securables
                         .OrderBy(a => $"{a}")
                         .ToArray();

            Charset = charset;

            var unicodeUpToBytesInSize = 1;

            switch (charset)
            {
            case PermissionMapCharset.Binary:
                CharsetValue = "01".ToCharArray();
                break;

            case PermissionMapCharset.Octal:
                CharsetValue = "01234567".ToCharArray();
                break;

            case PermissionMapCharset.Decimal:
                CharsetValue = "0123456789".ToCharArray();
                break;

            case PermissionMapCharset.Hexadecimal:
                CharsetValue = "0123456789abcdef".ToCharArray();
                break;

            case PermissionMapCharset.Letters:
                CharsetValue = "abcdefghijklmnopqrstuvwxyz".ToCharArray();
                break;

            case PermissionMapCharset.NumbersAndLetters:
                CharsetValue = "0123456789abcdefghijklmnopqrstuvwxyz".ToCharArray();
                break;

            case PermissionMapCharset.LettersCaseSensitive:
                CharsetValue = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".ToCharArray();
                break;

            case PermissionMapCharset.NumbersAndLettersCaseSensitive:
                CharsetValue = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".ToCharArray();
                break;

            case PermissionMapCharset.VisibleAscii:
                CharsetValue = Encoding
                               .ASCII
                               .GetAllEncodedStrings()
                               .Where(a =>
                                      a.Value.Code > 32 &&
                                      a.Value.Code < 127)
                               .Select(a => a.Value.Text[0])
                               .ToArray();
                break;

            case PermissionMapCharset.UnicodeUpTo3BytesInSize:
                unicodeUpToBytesInSize = 3;
                goto case PermissionMapCharset.UnicodeUpTo1BytesInSize;

            case PermissionMapCharset.UnicodeUpTo2BytesInSize:
                unicodeUpToBytesInSize = 2;
                goto case PermissionMapCharset.UnicodeUpTo1BytesInSize;

            case PermissionMapCharset.UnicodeUpTo1BytesInSize:
                CharsetValue = Encoding
                               .UTF8
                               .GetAllEncodedStrings()
                               .Where(a =>
                                      a.Value.Code >= 32 &&
                                      a.Value.Size >= 1 &&
                                      a.Value.Size <= unicodeUpToBytesInSize)
                               .Select(a => a.Value.Text[0])
                               .ToArray();
                break;

            case PermissionMapCharset.Custom:
                if (charsetValue == null || charsetValue.Length < 2)
                {
                    throw new ArgumentNullException(nameof(charset));
                }

                var charsetValueArray = charsetValue.ToCharArray();

                if (charsetValueArray.Distinct().Count() != charsetValue.Length)
                {
                    throw new ArgumentException($"Duplications found in the {nameof(charset)}.");
                }

                CharsetValue = charsetValueArray;
                break;

            default:
                throw new ArgumentOutOfRangeException(nameof(charset), charset, null);
            }

            var maxValueBinary  = new string('1', ChunkBinarySize);
            var maxValueInteger = Convert.ToUInt64(maxValueBinary, 2);
            var maxValue        = maxValueInteger.ConvertToNumericBase(CharsetValue);

            _chunkSize = maxValue.Length;
            _mapSize   = maxValue.Length * (int)System.Math.Ceiling(
                Permissions.Length * Securables.Length / (double)maxValueBinary.Length);
        }