using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Dasync.Collections;
using Microsoft.Extensions.Logging;

namespace MaddoShared
{
    public class ImageCreationStuff(ILogger<ImageCreationStuff> logger, PicSettings picSettings, ImageCreatorSharp imageCreatorService)
    {
        public class Options
        {
            public bool AggiornaSottodirectory { get; set; }
            public bool CreaSottocartelle { get; set; }

            public int FilePerCartella { get; set; }
            public string SuffissoCartelle { get; set; }
            public int CifreContatore { get; set; }
            public NumerazioneType NumerazioneType { get; set; }
            public string SourcePath { get; set; }
            public string DestinationPath { get; set; }

            public int MaxThreads { get; set; }
            public int ChunksSize { get; set; }
            public bool LinearExecution { get; set; }
        }

        public async Task<string> CreaCatalogoParallel(Options options, ConcurrentBag<string> results,
            EventHandler<Tuple<string, int>> updateEvent,
            CancellationToken cancellationToken = default(CancellationToken))
        {
            var stopwatch = new Stopwatch();
            stopwatch.Start();
            // todo immagini counter
            //todo set label
            await ProcessImagesParallel(options, results, updateEvent, cancellationToken);

            // todo set finito label
            stopwatch.Stop();

            return
                $"{stopwatch.Elapsed.Hours}h {stopwatch.Elapsed.Minutes}m ${stopwatch.Elapsed.Seconds}s ({stopwatch.Elapsed.TotalSeconds}s)";
        }

        public async Task ProcessImagesParallel(
            Options options,
            ConcurrentBag<string> results,
            EventHandler<Tuple<string, int>> updateEvent,
            CancellationToken cancellationToken = default)
        {
            List<FileData> dataToProcess = GetFilesToProcess(options);

            // int threads = options.MaxThreads == 0 ? Environment.ProcessorCount * 2 : options.MaxThreads;
            int threads = options.MaxThreads;
            
            Bitmap logoBmp = null;
            // Load Logo
            if (picSettings.LogoAggiungi & File.Exists(picSettings.LogoNomeFile))
            {
                logoBmp = new Bitmap(picSettings.LogoNomeFile);
            }
            
            Func<FileData, Task> processFile = async fileData =>
            {
                using var logoCopy = logoBmp.Clone(new Rectangle(0, 0, logoBmp.Width, logoBmp.Height),
                    logoBmp.PixelFormat);
                
                var imgState = new ImageState
                {
                    WorkFile = fileData.File,
                    DestDir = fileData.Directory,
                };

                await imageCreatorService.CreaImmagineThread(imgState, logoCopy);
                
                // using var imgCreator = new ImageCreatorSharp(fileData.File, fileData.Directory, picSettings);
                // await imgCreator.CreaImmagineThread(fileData.File.Name, logoCopy);
                
                results.Add(fileData.File.Name);

                try
                {
                    updateEvent?.Invoke(this, new Tuple<string, int>(fileData.File.Name, dataToProcess.Count));
                }
                catch (Exception e)
                {
                    logger.LogError(e, "Error in reporting update");
                    throw;
                }
                // finally
                // {
                //     imgCreator = null;
                // }
            };

            if (options.LinearExecution)
            {
                foreach (var fileData in dataToProcess)
                    await processFile(fileData);
            }
            else
            {
                var chunks = options.ChunksSize > 0
                    ? SplitList(dataToProcess, options.ChunksSize)
                    : new List<List<FileData>> { dataToProcess };

                foreach (var chunk in chunks)
                {
                    await chunk.ParallelForEachAsync(
                        processFile,
                        maxDegreeOfParallelism: threads,
                        false,
                        cancellationToken);
                    
                    chunk.Clear();
                    GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, blocking: false, compacting: false);
                }
            }

            try
            {
                logoBmp?.Dispose();
            }
            catch (Exception e)
            {
                logger.LogError(e, "Error in disposing the logo");
            }
           
        }

        private List<FileData> GetFilesToProcess(Options options)
        {
            if (options.AggiornaSottodirectory && options.CreaSottocartelle)
            {
                var helper = new FileHelperSharp();
                return helper.GetFilesRecursive(
                    new DirectoryInfo(options.SourcePath),
                    new DirectoryInfo(options.DestinationPath),
                    "*.jpg",
                    new FileHelperOptions
                    {
                        FilesPerFolder = options.FilePerCartella,
                        Suffix = options.SuffissoCartelle,
                        CounterSize = options.CifreContatore,
                        NumerationType = options.NumerazioneType
                    });
            }

            var files = Directory.EnumerateFiles(
                options.SourcePath,
                "*.jpg",
                options.AggiornaSottodirectory ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly);

            return files.Select(x =>
            {
                var fInfo = new FileInfo(x);
                var filePath = fInfo.DirectoryName;
                var trimmedSourcePath = options.SourcePath.TrimEnd('\\');
                var newFilePath = fInfo.FullName.Replace(trimmedSourcePath, "").TrimStart('\\');
                newFilePath = Path.Combine(options.DestinationPath, newFilePath);

                var destFolderPath = new FileInfo(newFilePath).DirectoryName;
                var destFolderInfo = new DirectoryInfo(destFolderPath);
                destFolderInfo.EnsureDirectoryExists();

                return new FileData(fInfo, new DirectoryInfo(new FileInfo(newFilePath).DirectoryName));
                
                // var destDir = new FileInfo(newFilePath).Directory!;
                // destDir.Create(); // Ensure exists
                //
                // return new FileData(fInfo, destDir);
            }).ToList();
        }

        // public async Task CreaImmaginiParallel(Options options, ConcurrentBag<string> results,
        //     EventHandler<Tuple<string, int>> updateEvent,
        //     CancellationToken cancellationToken = default(CancellationToken))
        // {
        //     var dataToProcess = new List<FileData>();
        //     if (options.AggiornaSottodirectory && options.CreaSottocartelle)
        //     {
        //         var helper = new FileHelperSharp();
        //         dataToProcess = helper.GetFilesRecursive(new DirectoryInfo(options.SourcePath),
        //             new DirectoryInfo(options.DestinationPath),
        //             "*.jpg", new FileHelperOptions()
        //             {
        //                 FilesPerFolder = options.FilePerCartella,
        //                 Suffix = options.SuffissoCartelle,
        //                 CounterSize = options.CifreContatore,
        //                 NumerationType = options.NumerazioneType
        //             });
        //     }
        //     else if (!options.CreaSottocartelle)
        //     {
        //         var files = Directory.EnumerateFiles(options.SourcePath, "*.jpg",
        //             options.AggiornaSottodirectory
        //                 ? SearchOption.AllDirectories
        //                 : SearchOption.TopDirectoryOnly);
        //
        //         dataToProcess = files.Select(x =>
        //         {
        //             var fInfo = new FileInfo(x);
        //             var filePath = fInfo.DirectoryName;
        //             var trimmedSourcePath = options.SourcePath.TrimEnd('\\');
        //             var newFilePath = fInfo.FullName.Replace(trimmedSourcePath, "").TrimStart('\\');
        //             newFilePath = Path.Combine(options.DestinationPath, newFilePath);
        //
        //             var destFolderPath = new FileInfo(newFilePath).DirectoryName;
        //             var destFolderInfo = new DirectoryInfo(destFolderPath);
        //             destFolderInfo.EnsureDirectoryExists();
        //
        //             return new FileData(fInfo, new DirectoryInfo(new FileInfo(newFilePath).DirectoryName));
        //         }).ToList();
        //
        //         //// TODO
        //         //dataToProcess =
        //         //    (from f in Directory.EnumerateFiles(options.SourcePath, "*.jpg",
        //         //            options.AggiornaSottodirectory
        //         //                ? SearchOption.AllDirectories
        //         //                : SearchOption.TopDirectoryOnly)
        //         //     select new FileData(new FileInfo(f),
        //         //         new DirectoryInfo(options.DestinationPath.PathCombine(
        //         //             new FileInfo(f).DirectoryName.Replace(options.SourcePath.TrimEnd(new char[] {'\\'}), "")
        //         //             )
        //         //         )
        //         //         )
        //         //        )
        //         //    .ToList();
        //     }
        //
        //     var threads = options.MaxThreads == 0 ? Environment.ProcessorCount * 2 : options.MaxThreads;
        //     var scheduler = new ConcurrentExclusiveSchedulerPair(TaskScheduler.Default, threads)
        //         .ConcurrentScheduler;
        //
        //     //var allTasks = new List<Task>();
        //     var test = from d in dataToProcess
        //         select Task.Factory.StartNew(async () =>
        //         {
        //             var imgCreator = new ImageCreatorSharp(d.File, d.Directory);
        //             await imgCreator.CreaImmagineThread(d.File.Name);
        //
        //             //await new ImageCreatorSharp(d.File, d.Directory).CreaImmagineThread(d.File.Name);
        //
        //             imgCreator = null;
        //
        //             //var imgC = new ImageCreatorSharp(d.File, d.Directory);
        //             //imgC.CreaImmagineThread(d.File.Name);
        //             //imgC = null;
        //         }, CancellationToken.None, TaskCreationOptions.None, scheduler);
        //
        //     //int count = 0;
        //
        //     if (options.LinearExecution)
        //     {
        //         foreach (var task in test)
        //         {
        //             await task;
        //         }
        //     }
        //     else
        //     {
        //         if (options.ChunksSize == 0)
        //         {
        //             //var opts = new ParallelOptions() { MaxDegreeOfParallelism = threads, CancellationToken = cancellationToken, TaskScheduler = scheduler};
        //             await dataToProcess.ParallelForEachAsync(async fileData =>
        //             {
        //                 var imgCreator = new ImageCreatorSharp(fileData.File, fileData.Directory);
        //                 await imgCreator.CreaImmagineThread(fileData.File.Name);
        //                 // await new ImageCreatorSharp(fileData.File, fileData.Directory).CreaImmagineThread(fileData.File.Name);
        //                 results.Add(fileData.File.Name);
        //                 //count = Interlocked.Increment(ref count);
        //
        //                 try
        //                 {
        //                     updateEvent?.Invoke(this, new Tuple<string, int>(fileData.File.Name, dataToProcess.Count));
        //                 }
        //                 catch (Exception e)
        //                 {
        //                     logger.LogError(e, "Error in reporting update");
        //                     //Console.WriteLine(e);
        //                     throw;
        //                 }
        //                 finally
        //                 {
        //                     // Disposing of the creator
        //                     imgCreator = null;
        //                 }
        //             }, maxDegreeOfParallelism: threads, false, cancellationToken);
        //         }
        //         else
        //         {
        //             var asdf = SplitList(dataToProcess.ToList(), options.ChunksSize).ToList();
        //
        //
        //             foreach (var sdaf in asdf)
        //             {
        //                 await sdaf.ParallelForEachAsync(async fileData =>
        //                 {
        //                     var imgCreator = new ImageCreatorSharp(fileData.File, fileData.Directory);
        //                     await imgCreator.CreaImmagineThread(fileData.File.Name);
        //                     // await new ImageCreatorSharp(fileData.File, fileData.Directory).CreaImmagineThread(
        //                     //     fileData.File.Name);
        //                     results.Add(fileData.File.Name);
        //                     //count = Interlocked.Increment(ref count);
        //                     try
        //                     {
        //                         updateEvent?.Invoke(this,
        //                             new Tuple<string, int>(fileData.File.Name, dataToProcess.Count));
        //                     }
        //                     catch (Exception e)
        //                     {
        //                         logger.LogError("Error in reporting update");
        //                         //Console.WriteLine(e);
        //                         throw;
        //                     }
        //                     finally
        //                     {
        //                         imgCreator = null;
        //                     }
        //                 }, maxDegreeOfParallelism: threads, false, cancellationToken);
        //             }
        //         }
        //     }
        // }

        public static IEnumerable<List<T>> SplitList<T>(List<T> bigList, int nSize = 3)
        {
            for (int i = 0; i < bigList.Count; i += nSize)
            {
                yield return bigList.GetRange(i, Math.Min(nSize, bigList.Count - i));
            }
        }
    }
}