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

namespace ZionFoundation.Components
{
    public class Folder : IComponent
    {
        #region Private properties
        private string path;
        private EventHandler updated;
        private bool monitor;
        #endregion

        #region Public properties
        public string Name { get; set; }
        public string Status { get; set; }
        public string[] ExtensionsToIgnore { get; set; }
        public bool Monitor { get => monitor; set => monitor = value; }
        #endregion

        #region Custom properties
        public bool Accessible
        {
            get
            {
                if (!System.IO.Directory.Exists(this.FullPath))
                    return false;

                return true;
            }
        }
        public string Directory
        {
            get
            {
                return string.Format(@"{0}", path);
            }
        }
        public string FullPath
        {
            get
            {
                return string.Format(@"{0}\{1}", path, this.Name);
            }
        }
        #endregion

        #region Neasted objects
        public List<IComponent> neasted;
        public List<File> Files
        {
            get
            {
                if (neasted == null)
                    neasted = new List<IComponent>();
                if (neasted.ToList().Where(child => child.GetType() != this.GetType()).Count() == 0)
                    neasted.AddRange(LoadFiles());
                return (from item in neasted.ToList()
                        where item.GetType() != this.GetType()
                        select (File)item).OrderBy(x => x.Name).ToList();
            }
        }
        public List<Folder> Folders
        {
            get
            {
                if (neasted == null)
                    neasted = new List<IComponent>();
                if (neasted.Where(child => child.GetType() == this.GetType()).Count() == 0)
                {
                    var itens = LoadFolders();
                    neasted.AddRange(itens);
                }
                return (from item in neasted.ToList()
                        where item.GetType() == this.GetType()
                        select (Folder)item).OrderBy(x => x.Name).ToList();
            }
        }
        public void Append(IComponent component)
        {
            if (component is Components.File
                 && Files.Where(file => file.Name == ((File)component).Name 
                    && file.Extension == ((File)component).Extension).Count() == 0)
            {
                this.neasted.Add(component);
            }
            else if (component is Components.Folder 
                && Folders.Where(folder => folder.Name == ((Folder)component).Name).Count() == 0)
            {
                this.neasted.Add(component);
            }

            if (updated != null)
                updated(component, new EventArgs());
        }
        public List<IComponent> Content { get => neasted; }
        #endregion

        #region Task
        private GlobalTasks.FolderMonitoring FolderMonitor;
        private void StartMonitoring()
        {
            FolderMonitor = new GlobalTasks.FolderMonitoring(FullPath, Monitor_Trigger);
            if (this.Monitor)
            {
                FolderMonitor.Initilize();
            }
        }
        public virtual void Monitor_Trigger(object sender, FileSystemEventArgs e)
        {
            if (!this.ExtensionsToIgnore.Contains(new System.IO.FileInfo(e.FullPath).Extension))
            {
                Append(IComponent.Build(e.FullPath, this.Monitor));
            }
        }
    #endregion

    #region Constructors
    public Folder(string path, bool monitor = true)
        {
            try
            {
                System.IO.DirectoryInfo info = new System.IO.DirectoryInfo(path);

                this.path = info.Parent.FullName;
                this.Name = info.Name;
                //this.Status = monitor ?  "1" : "0";
                //this.Monitor = monitor;
                BuildDirectory(path);
                //StartMonitoring();
            } catch
            {
                throw new Exception($"Não foi possível acessar o diretório '{path}'");
            }
        }
        public Folder LoadContent()
        {
            LoadFiles();
            LoadFolders();
            return this;
        }

        public virtual List<IComponent> LoadFiles() { return null; }
        public virtual List<IComponent> LoadFolders() { return null; }
        #endregion

        #region Controllers
        public void Watch()
        {
            FolderMonitor?.Start();
        }
        public void Suspend()
        {
            FolderMonitor?.Stop();
        }

        public bool Alive()
        {
            return FolderMonitor.isRunning;
        }
        #endregion

        private void BuildDirectory(string path)
        {
            if (path.StartsWith(@"\\"))
            {
                BuildNetworkDirectory(path);
            }
            else
            {
                BuildLocalDirectory(path);
            }
        }
        private void BuildLocalDirectory(string path)
        {
            string validpath = string.Empty;
            string[] folders = path.Split('\\');

            foreach (string folder in folders)
            {
                validpath = string.Format(@"{0}\{1}", validpath, folder).Trim('\\');
                if (!System.IO.Directory.Exists(validpath))
                {
                    System.IO.Directory.CreateDirectory(validpath.TrimEnd('\\'));
                    GrantPermissionToFolder(validpath);
                }
            }
        }
        private void BuildNetworkDirectory(string path)
        {
            string validpath = @"\";
            string[] folders = path.Split('\\');

            foreach (string folder in folders)
            {
                if (folder != string.Empty)
                {
                    validpath = string.Format(@"{0}\{1}", validpath, folder).TrimEnd('\\');

                    if (!System.IO.Directory.Exists(validpath) && validpath.Count(f => f == '\\') > 2)
                    {
                        System.IO.Directory.CreateDirectory(validpath);
                        GrantPermissionToFolder(validpath);
                    }
                }
            }
        }
        private void GrantPermissionToFolder(string path)
        {
            try
            {
                System.Security.AccessControl.DirectorySecurity ds = System.IO.Directory.GetAccessControl(path);
                ds.AddAccessRule(
                    new System.Security.AccessControl.FileSystemAccessRule(
                        new System.Security.Principal.SecurityIdentifier(System.Security.Principal.WellKnownSidType.WorldSid, null),
                        System.Security.AccessControl.FileSystemRights.FullControl,
                        System.Security.AccessControl.InheritanceFlags.ContainerInherit | System.Security.AccessControl.InheritanceFlags.ObjectInherit,
                        System.Security.AccessControl.PropagationFlags.None,
                        System.Security.AccessControl.AccessControlType.Allow));
                System.IO.Directory.SetAccessControl(path, ds);
            }
            catch (Exception ex)
            {

            }
        }

        public static void CreateDirectory(string path)
        {
            new Folder(path, false);
        }
        public static List<string> SearchForFiles(string path, string extension, bool subfolders = true)
        {
            List<string> files = new List<string>();

            if (subfolders)
                System.IO.Directory.GetDirectories(path).ToList().ForEach(dir =>
                {
                    files.AddRange(SearchForFiles(dir, extension));
                });

            files.AddRange(System.IO.Directory.GetFiles(path).ToList().Where(x => x.ToLower().EndsWith(extension)).ToList());

            return files;
        }
    } 
}
