/// <summary>
 /// Builds an excel worksheet and returns its bytes.
 /// </summary>
 /// <param name="multiCollections">
 /// The collections of different elements, to be inserted in the worksheet.
 /// </param>
 /// <returns>The built worksheet's bytes.</returns>
 public static IEnumerable <byte> ToComplexExcelWorksheet(
     IEnumerable <IEnumerable <object> > multiCollections,
     ExcelWorksheetOptions options = default)
 {
     if (multiCollections.IsNullOrEmpty())
     {
         return(default);
        /// <summary>
        /// Builds an excel worksheet and returns its bytes.
        /// </summary>
        /// <param name="multiCollections">
        /// The collections of different elements, to be inserted in the worksheet.
        /// </param>
        /// <returns>The built worksheet's bytes.</returns>
        public static IEnumerable <byte> ToComplexExcelWorksheet(
            IEnumerable <IEnumerable <object> > multiCollections,
            Action <ExcelWorksheetOptions> optionsExpression = default)
        {
            var options = new ExcelWorksheetOptions();

            if (optionsExpression != default)
            {
                optionsExpression(options);
            }

            return(ToComplexExcelWorksheet(multiCollections, options));
        }
        /// <summary>
        /// Builds an excel worksheet and returns its bytes.
        /// </summary>
        /// <typeparam name="TSource">The type, that should be represented in the worksheet.</typeparam>
        /// <param name="source">The collection of elements, to be inserted in the worksheet.</param>
        /// <param name="optionsExpression">(optional) Style options expression.</param>
        /// <returns>The built worksheet's bytes.</returns>
        public static IEnumerable <byte> ToExcelWorksheet <TSource>(
            this IEnumerable <TSource> source,
            Action <ExcelWorksheetOptions> optionsExpression = default)
            where TSource : class
        {
            var options = new ExcelWorksheetOptions();

            if (optionsExpression != default)
            {
                optionsExpression(options);
            }

            return(ToExcelWorksheet(source, options));
        }
        /// <summary>
        /// Builds an excel worksheet and returns its bytes.
        /// </summary>
        /// <typeparam name="TSource">The type, that should be represented in the worksheet.</typeparam>
        /// <param name="source">The collection of elements, to be inserted in the worksheet.</param>
        /// <param name="optionsExpression">(optional) Style options expression.</param>
        /// <returns>The built worksheet's bytes.</returns>
        public static IEnumerable <byte> ToExcelWorksheet <TSource>(
            this IEnumerable <TSource> source,
            ExcelWorksheetOptions options = default)
            where TSource : class
        {
            options = options ?? new ExcelWorksheetOptions();

            if (typeof(IEnumerable).IsAssignableFrom(typeof(TSource)))
            {
                var multiCollection = new List <List <object> >();
                var matrix          = source as IEnumerable <IEnumerable>;

                foreach (var row in matrix)
                {
                    var currentRow = new List <object>();

                    multiCollection.Add(currentRow);

                    foreach (var column in row)
                    {
                        currentRow.Add(column);
                    }
                }

                return(ToComplexExcelWorksheet(multiCollection, options));
            }

            using (var application = new ExcelPackage())
            {
                application.Workbook.Properties.Title = options.SheetName;
                var sheet = application.Workbook.Worksheets.Add(options.SheetName);

                var allowedProperties = typeof(TSource)
                                        .GetProperties()
                                        .Select(property => new
                {
                    Property   = property,
                    Attributes = property.GetCustomAttributes(false)
                })
                                        .Where(o => o.Attributes
                                               .All(attribute => !(attribute is ExcelIgnoreAttribute)))
                                        .ToDictionary(a => a.Property, a => a.Attributes.Cast <Attribute>());

                var headerFont = new Font(
                    options.SheetHeaderFontFamily,
                    options.SheetHeaderFontSize,
                    FontStyle.Bold);
                GenerateExcelHeader(
                    sheet,
                    allowedProperties,
                    headerFont,
                    options.SheetHeaderColor);

                var bodyFont = new Font(options.SheetBodyFontFamily, options.SheetBodyFontSize);
                GenerateExcelBody(sheet, source, allowedProperties, bodyFont);

                FormatSheet(sheet);

                currentRow = 1;

                return(application.GetAsByteArray());
            }
        }