using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;

namespace ImageCatalog
{
    public class FileHelper
    {
        // Private dirSourceDest As Dictionary(Of FileInfo, DirectoryInfo)
        private int filesPerFolder;
        private string suffix;
        private int counterSize;
        private int numerationType;
        private string filter;
        private bool separateFiles;
        private string extensions = "*.jpg,*.png,*.gif";

        public enum numerazione
        {
            Progressiva,
            Files
        }
        /// <summary>
    /// Preparazione per la separazione
    /// </summary>
    /// <param name="filesPerFolder"></param>
    /// <param name="suffix"></param>
    /// <param name="counterSize"></param>
    /// <param name="numerationType"></param>
    /// <remarks></remarks>
        public FileHelper(int filesPerFolder, string suffix, int counterSize, int numerationType)
        {
            this.filesPerFolder = filesPerFolder;
            this.suffix = suffix;
            this.counterSize = counterSize;
            this.numerationType = numerationType;
            separateFiles = true;
        }

        /// <summary>
    /// nessuna separazione
    /// </summary>
    /// <remarks></remarks>
        public FileHelper()
        {
            separateFiles = false;
        }

        public Dictionary<FileInfo, DirectoryInfo> GetFilesRecursive(DirectoryInfo root, DirectoryInfo destRoot, string filter)
        {
            var dirSourceDest = new Dictionary<FileInfo, DirectoryInfo>();
            var result = new List<FileInfo>();

            // Dim stack As New Stack(Of DirectoryInfo)
            var stack = new Stack<KeyValuePair<DirectoryInfo, DirectoryInfo>>();
            this.filter = filter;
            var pair = new KeyValuePair<DirectoryInfo, DirectoryInfo>();


            // stack.Push(root)
            stack.Push(new KeyValuePair<DirectoryInfo, DirectoryInfo>(root, destRoot));
            while (stack.Count > 0)
            {
                var curDirKV = stack.Pop();
                // curDirKP = stack.Pop()
                var dir = curDirKV.Key;
                var dDir = curDirKV.Value;
                try
                {
                    // result.AddRange(dir.GetFiles(filter, SearchOption.TopDirectoryOnly))
                    // dividere file qui
                    if (filesPerFolder > 0 & separateFiles)
                    {
                        appendDictionary(dirSourceDest, dividiFilesInDir(dir, dDir));
                    }
                    else
                    {
                        appendDictionary(dirSourceDest, getAllFilesInDir(dir, dDir));
                    }

                    foreach (DirectoryInfo subDirectory in dir.GetDirectories())
                        stack.Push(new KeyValuePair<DirectoryInfo, DirectoryInfo>(subDirectory, new DirectoryInfo(Path.Combine(dDir.FullName, subDirectory.Name))));
                }
                catch (Exception ex)
                {
                    var e = ex.Demystify();
                    Console.WriteLine(e);
                    Console.WriteLine(e.Message);
                    Console.WriteLine(e.StackTrace);
                }
            }

            return dirSourceDest;
        }

        // Public Class FileData
        // Public File As FileInfo
        // Public Directory As DirectoryInfo
        // Public Sub New(newFile As FileInfo, newDirectory As DirectoryInfo)
        // File = newFile
        // Directory = newDirectory
        // End Sub
        // End Class

        // Public Function GetFilesRecursiveParallel(ByVal root As DirectoryInfo, ByVal destRoot As DirectoryInfo, ByVal filter As String) As List(Of FileData)


        // Dim dirSourceDest As New ConcurrentDictionary(Of FileInfo, DirectoryInfo)
        // Dim result As New List(Of FileInfo)

        // 'Dim stack As New Stack(Of DirectoryInfo)
        // Dim stack As New Stack(Of KeyValuePair(Of DirectoryInfo, DirectoryInfo))


        // Me.filter = filter
        // Dim pair As New KeyValuePair(Of DirectoryInfo, DirectoryInfo)


        // 'stack.Push(root)
        // stack.Push(New KeyValuePair(Of DirectoryInfo, DirectoryInfo)(root, destRoot))

        // Do While (stack.Count > 0)
        // Dim curDirKV As KeyValuePair(Of DirectoryInfo, DirectoryInfo) = stack.Pop
        // 'curDirKP = stack.Pop()
        // Dim dir As DirectoryInfo = curDirKV.Key
        // Dim dDir As DirectoryInfo = curDirKV.Value
        // Try
        // 'result.AddRange(dir.GetFiles(filter, SearchOption.TopDirectoryOnly))
        // ' dividere file qui
        // If filesPerFolder > 0 And separateFiles Then
        // AppendDictionaryConcurrent(dirSourceDest, DividiFilesInDirConcurrent(dir, dDir))
        // Else
        // AppendDictionaryConcurrent(dirSourceDest, DividiFilesInDirConcurrent(dir, dDir))
        // End If

        // For Each subDirectory As DirectoryInfo In dir.GetDirectories
        // stack.Push(New KeyValuePair(Of DirectoryInfo, DirectoryInfo)(subDirectory, New DirectoryInfo(Path.Combine(dDir.FullName, subDirectory.Name))))

        // Next
        // Catch ex As Exception
        // ' TODO: FARE QUALCOSA
        // End Try
        // Loop

        // Dim resultData As New List(Of FileData)
        // resultData.AddRange(From p In dirSourceDest Select New FileData(p.Key, p.Value))
        // Return resultData
        // 'Return dirSourceDest
        // End Function

        public Dictionary<FileInfo, DirectoryInfo> appendDictionary(Dictionary<FileInfo, DirectoryInfo> dictA, Dictionary<FileInfo, DirectoryInfo> dictB)
        {
            foreach (KeyValuePair<FileInfo, DirectoryInfo> pair in dictB)
                dictA.Add(pair.Key, pair.Value);
            return dictA;
        }

        // Public Function AppendDictionaryConcurrent(ByVal dictA As ConcurrentDictionary(Of FileInfo, DirectoryInfo), ByVal dictB As ConcurrentDictionary(Of FileInfo, DirectoryInfo)) As ConcurrentDictionary(Of FileInfo, DirectoryInfo)
        // For Each pair As KeyValuePair(Of FileInfo, DirectoryInfo) In dictB
        // dictA.TryAdd(pair.Key, pair.Value)
        // 'dictA.Add(pair.Key, pair.Value)
        // Next
        // Return dictA
        // End Function

        public Dictionary<FileInfo, DirectoryInfo> getAllFilesInDir(DirectoryInfo dir, DirectoryInfo dirDest)
        {
            var dict = new Dictionary<FileInfo, DirectoryInfo>();
            foreach (FileInfo File in dir.GetFiles(filter))
                dict.Add(File, new DirectoryInfo(Path.Combine(dirDest.FullName, File.Name)));
            return dict;
        }

        private Dictionary<FileInfo, DirectoryInfo> dividiFilesInDir(DirectoryInfo dir, DirectoryInfo dirDest)
        {
            int filesCount = dir.GetFiles(filter).Count();
            int contaFilePerDir = 0;
            int contaDirPerDir = 0;
            string tempText = string.Empty;
            var foldersDict = new Dictionary<FileInfo, DirectoryInfo>();
            DirectoryInfo destDir;
            destDir = new DirectoryInfo(Path.Combine(dirDest.FullName));
            foreach (FileInfo file in dir.GetFiles(filter))
            {
                contaFilePerDir += 1;
                if (contaFilePerDir == contaDirPerDir * filesPerFolder + 1)
                {
                    contaDirPerDir += 1;
                    if (numerationType == (int)numerazione.Progressiva)
                    {
                        tempText = contaDirPerDir.ToString();
                    }
                    else
                    {
                        tempText = (contaDirPerDir * filesPerFolder).ToString();
                    }

                    int i;
                    var loopTo = counterSize - tempText.Length;
                    for (i = 1; i <= loopTo; i++)
                        tempText = "0" + tempText;
                    destDir = new DirectoryInfo(Path.Combine(dirDest.FullName, suffix + tempText));
                }

                if (!destDir.Exists)
                {
                    destDir.Create();
                }

                foldersDict.Add(file, destDir);
            }

            return foldersDict;
        }

        private ConcurrentDictionary<FileInfo, DirectoryInfo> DividiFilesInDirConcurrent(DirectoryInfo dir, DirectoryInfo dirDest)
        {
            int filesCount = dir.GetFiles(filter).Count();
            int contaFilePerDir = 0;
            int contaDirPerDir = 0;
            string tempText = string.Empty;
            var foldersDict = new ConcurrentDictionary<FileInfo, DirectoryInfo>();
            DirectoryInfo destDir;
            destDir = new DirectoryInfo(Path.Combine(dirDest.FullName));
            foreach (FileInfo file in dir.GetFiles(filter))
            {
                contaFilePerDir += 1;
                if (contaFilePerDir == contaDirPerDir * filesPerFolder + 1)
                {
                    contaDirPerDir += 1;
                    if (numerationType == (int)numerazione.Progressiva)
                    {
                        tempText = contaDirPerDir.ToString();
                    }
                    else
                    {
                        tempText = (contaDirPerDir * filesPerFolder).ToString();
                    }

                    int i;
                    var loopTo = counterSize - tempText.Length;
                    for (i = 1; i <= loopTo; i++)
                        tempText = "0" + tempText;
                    destDir = new DirectoryInfo(Path.Combine(dirDest.FullName, suffix + tempText));
                }

                if (!destDir.Exists)
                {
                    destDir.Create();
                }

                foldersDict.TryAdd(file, destDir);
            }

            return foldersDict;
        }
    }
}