using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
// Using
using System.IO;

namespace ClearCache1C
{
    public partial class FormMain : Form
    {
        public FormMain()
        {
            StartPosition = FormStartPosition.CenterScreen;
            InitializeComponent();            
        }
        // ******************* Classes *******************
        public class BasesListItem
        {
            public string Name { get; set; }
            public string Path { get; set; }
            public string Id { get; set; }
            public bool FolderExists { get; set; }
            public float FolderSize { get; set; }
            public float CacheSize { get; set; }
            public List<string> FolderPaths { get; set; }
            public List<string> CachePaths { get; set; }
            public BasesListItem(string s_name, string s_path, string s_id, 
                bool s_folder_exists, 
                float s_folder_size, float s_cache_size,
                List<string> s_folder_paths, List<string> s_cache_paths)
            {
                Name = s_name;
                Path = s_path;
                Id = s_id;
                FolderExists = s_folder_exists;
                FolderSize = s_folder_size;
                CacheSize = s_cache_size;
                FolderPaths = s_folder_paths;
                CachePaths = s_cache_paths;
            }
            public override string ToString() {
                string f_size = String.Format("{0,3}", CacheSize.ToString("0"));
                return (CacheSize > 0  ? "[" + f_size + " Mb] ": "[------] ")
                    + Name;
            }
        }
        // ******************* Events *******************
        private void FormMain_Load(object sender, EventArgs e)
        {
            RefreshListBases();
            ToggleInfoView();
            SetToolTips();
        }
        // ****** LIST CONTROL BUTTONS
        private void ButtonSmartSelect_Click(object sender, EventArgs e)
        {
            ModListBases(true, true);
        }
        private void ButtonSelectAll_Click(object sender, EventArgs e)
        {
            ModListBases(true, false);
        }
        private void ButtonRemoveSelection_Click(object sender, EventArgs e)
        {
            ModListBases(false, false);
        }
        private void ButtonExpand_Click(object sender, EventArgs e)
        {
            ToggleInfoView();
        }
        private void ToggleInfoView()
        {
            if (Size.Width != 870) // is collapsed
            {
                Size = new Size(870, Size.Height);
                btnExpand.Text = "<\r\n<\r\n<";
                InfoBox.Visible = true;
                ShowSelectedInfo();
            }
            else
            {
                int calc_width = btnExpand.Location.X + btnExpand.Size.Width + 22;
                Size = new Size(calc_width, Size.Height);
                btnExpand.Text = ">\r\n>\r\n>";
                InfoBox.Visible = false;
            }
        }
        private void ButtonAbout_Click(object sender, EventArgs e)
        {
            FormAbout aboutForm = new FormAbout();
            aboutForm.ShowDialog();
        }
        // ****** MAIN BUTTONS
        private void ButtonClearCache_Click(object sender, EventArgs e)
        {
            if (ListBases.CheckedItems.Count == 0)
            {
                MessageBox.Show("Ничего не выбрано.", "Meh...", MessageBoxButtons.OK, MessageBoxIcon.Asterisk);
                return;
            }
            ClearFolders(false);
            RefreshListBases();
            ShowSelectedInfo();
        }
        private void ButtonPurgeAll_Click(object sender, EventArgs e)
        {
            if (ListBases.CheckedItems.Count == 0)
            {
                MessageBox.Show("Ничего не выбрано.", "Meh...", MessageBoxButtons.OK, MessageBoxIcon.Asterisk);
                return;
            }
            DialogResult dialogResult = MessageBox.Show("Это удалит все локальные настройки пользователя в выбранных базах. \r\nВы уверены, что хотите этого?", "Внимание!", MessageBoxButtons.YesNo, MessageBoxIcon.Warning);
            if (dialogResult == DialogResult.Yes)
            {
                ClearFolders(true);
                RefreshListBases();
                ShowSelectedInfo();
            }
        }
        // ****** FOR DEBUG
        private void ListBases_SelectedIndexChanged(object sender, EventArgs e)
        {
            ShowSelectedInfo();
        }
        // ******************* MAIN  *******************
        private void ClearFolders(bool purge)
        {
            CheckedListBox.CheckedItemCollection items = ListBases.CheckedItems;

            int successfully_deleted = 0;
            int errors_on_delete = 0;
            float total_size_deleted = 0.0f;

            List<string> list_success = new List<string>();
            List<string> list_error = new List<string>();
            List<string> list_exceptions = new List<string>();

            for (int i = 0; i < items.Count; i++)
            {
                BasesListItem base_item = items[i] as BasesListItem;
                (bool is_error, float deleted_size, List<string> exceptions) = DeleteFoldersContent(purge ? base_item.FolderPaths : base_item.CachePaths);
                if (!is_error)
                {
                    successfully_deleted++;
                    list_success.Add(base_item.Name);
                }
                else
                {
                    errors_on_delete++;
                    list_error.Add(base_item.Name);
                    list_exceptions.AddRange(exceptions);
                }
                total_size_deleted += deleted_size;
            }
            ShowResults(successfully_deleted, errors_on_delete, total_size_deleted, list_success, list_error, list_exceptions);
        }
        private void ShowResults(int successfully_deleted, int errors_on_delete, float total_size_deleted, 
            List<string> list_success, List<string> list_error, List<string> list_exceptions)
        {
            FormResult resultForm = new FormResult();
            resultForm.SetInfoResult(successfully_deleted, errors_on_delete, total_size_deleted, 
                list_success, list_error, list_exceptions);
            resultForm.ShowDialog();
        }
        private (bool is_error, float deleted_size, List<string> exceptions) DeleteFoldersContent(List<string> ListPaths)
        {
            bool is_error = false;
            float deleted_size = 0.0f;
            List<string> exceptions = new List<string>();

            foreach (string folder_path in ListPaths)
            {
                long folder_size = DirSize(new DirectoryInfo(folder_path));
                try
                {
                    Directory.Delete(folder_path, true);
                    deleted_size += folder_size;
                }
                catch (Exception e)
                {
                    is_error = true;
                    exceptions.Add(e.ToString());
                }
            }
            deleted_size = deleted_size / 1024 / 1024;
            return (is_error, deleted_size, exceptions);
        }

        // ******************* Added functions *******************
        private void ShowSelectedInfo()
        {
            if (!InfoBox.Visible) { return; }
            InfoBox.ResetText();
            BasesListItem curItem = ListBases.SelectedItem as BasesListItem;
            if (curItem is null) { return; }
            /*
            Console.WriteLine("Name: " + curItem.Name);
            Console.WriteLine("ID: " + curItem.Id);
            Console.WriteLine("Connect string: " + curItem.Path);
            Console.WriteLine("Cache exists: " + curItem.FolderExists);
            Console.WriteLine("Size total: " + curItem.FolderSize.ToString("0.00"));
            Console.WriteLine("Size cache: " + curItem.CacheSize.ToString("0.00"));
            Console.WriteLine("Root Folders: " + curItem.FolderPaths.Count);
            Console.WriteLine("Cache Folders: " + curItem.CachePaths.Count);
            */
            InfoBox.AppendText("--- Информация о базе ---");
            InfoBox.AppendText("\r\nИмя: " + curItem.Name);
            InfoBox.AppendText("\r\nID: " + curItem.Id);
            InfoBox.AppendText("\r\nСтрока подключения: " + curItem.Path);

            InfoBox.AppendText("\r\n");
            InfoBox.AppendText("\r\nКэш существует: " + ((curItem.CacheSize > 0) ? "Да" : "Нет"));
            InfoBox.AppendText("\r\nРазмер кэша: " + curItem.CacheSize.ToString("0.00") + " Mb");
            InfoBox.AppendText("\r\nРазмер всего: " + curItem.FolderSize.ToString("0.00") + " Mb");

            InfoBox.AppendText("\r\n");
            InfoBox.AppendText("\r\nКорневые каталоги: ");
            foreach (string s in curItem.FolderPaths)
            {
                InfoBox.AppendText("\r\n > " + s);
            }
            InfoBox.AppendText("\r\n");

            InfoBox.AppendText("\r\nКаталоги кэша: ");
            foreach (string s in curItem.CachePaths)
            {
                InfoBox.AppendText("\r\n > " + s);
            }
            InfoBox.AppendText("\r\n");
        }
        private void ModListBases(bool isChecked, bool onlyWithCache)
        {
            for (int i = 0; i < ListBases.Items.Count; i++)
            {
                if (onlyWithCache)
                {
                    if ((ListBases.Items[i] as BasesListItem).CacheSize > 0)
                    {
                        ListBases.SetItemChecked(i, true);
                    }
                    else
                    {
                        ListBases.SetItemChecked(i, false);
                    }
                }
                else
                {
                    ListBases.SetItemChecked(i, isChecked);
                }
            }
        }
        private void RefreshListBases()
        {
            this.ListBases.Items.Clear();

            string base_path = @"%appdata%\1C\1CEStart\ibases.v8i";
            string[] lines = ReadFileContent(base_path);
            if (lines == null)
            {
                MessageBox.Show("Can't find ibases.v8i file in provided location: " + base_path, "Error reading file!",
                    MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }

            var bases_dict = GetBasesDictionary(lines);
            foreach (string key in bases_dict.Keys)
            {
                var value = bases_dict[key];
                (bool s_folder_exists, 
                    float s_folder_size, float s_cache_size,
                    List<string> s_folder_paths, List<string> s_cache_paths) = GetLocalFolderInfo(key);
                ListBases.Items.Add(new BasesListItem(value.Name, value.Path, key,
                    s_folder_exists,
                    s_folder_size, s_cache_size,
                    s_folder_paths, s_cache_paths), false);
            }
        }
        private (bool s_folder_exists, 
            float s_folder_size, float s_cache_size,
            List<string> s_folder_paths, List<string> s_cache_paths) GetLocalFolderInfo(string key)
        {
            // Local path of cache is:
            // %APPDATA%\1C\1cv8\$Id
            // %LOCALAPPDATA%\1C\1cv8\$Id
            // var cache_folders = ("Config", "ConfigSave", "SICache", "vrs-cache");

            string base_path_local = @"%LOCALAPPDATA%\1C\1cv8\" + key;
            string base_path_roaming = @"%APPDATA%\1C\1cv8\" + key;            

            base_path_local = Environment.ExpandEnvironmentVariables(base_path_local);
            base_path_roaming = Environment.ExpandEnvironmentVariables(base_path_roaming);

            float s_folder_size = 0.0f;
            float s_cache_size = 0.0f;

            List<string> s_folder_paths = new List<string>();
            List<string> s_cache_paths = new List<string>();          

            bool s_folder_exists = (Directory.Exists(base_path_local) || Directory.Exists(base_path_roaming)) ? true : false;
            if (s_folder_exists)
            {
                s_cache_paths = GetFoldersById(base_path_local, base_path_roaming);
                if (s_cache_paths.Count > 0)
                {
                    foreach (string s in s_cache_paths)
                    {
                        s_cache_size += DirSize(new DirectoryInfo(s));
                    }
                }
                if (Directory.Exists(base_path_local))
                {
                    s_folder_size += DirSize(new DirectoryInfo(base_path_local));
                    s_folder_paths.Add(base_path_local);
                }
                if (Directory.Exists(base_path_roaming))
                {
                    s_folder_size += DirSize(new DirectoryInfo(base_path_roaming));
                    s_folder_paths.Add(base_path_roaming);
                }
            }
            s_folder_size = s_folder_size / 1024 / 1024; // Mb
            s_cache_size = s_cache_size / 1024 / 1024; // Mb
            return (s_folder_exists,
                s_folder_size, s_cache_size,
                s_folder_paths, s_cache_paths);
        }
        private List<string> GetFoldersById(string base_path_local, string base_path_roaming)
        {
            var found_folders = new List<string>();
            if (Directory.Exists(base_path_local))
            {
                found_folders.AddRange(Directory.GetDirectories(base_path_local, "Config", SearchOption.AllDirectories).ToList());
                found_folders.AddRange(Directory.GetDirectories(base_path_local, "ConfigSave", SearchOption.AllDirectories).ToList());
                found_folders.AddRange(Directory.GetDirectories(base_path_local, "SICache", SearchOption.AllDirectories).ToList());
            }
            if (Directory.Exists(base_path_roaming))
            {
                found_folders.AddRange(Directory.GetDirectories(base_path_roaming, "vrs-cache", SearchOption.AllDirectories).ToList());
            }
            return found_folders;
        }
        private Dictionary<string, (string Name, string Path)> GetBasesDictionary(string[] lines)
        {
            var bases_dict = new Dictionary<string, (string Name, string Path)>();
            var (name, id, path) = ("", "", "");
            foreach (string line in lines)
            {
                /* ---------- EXAMPLE
                [Торговля(Рабочая)]
                Connect=Srvr="winserv";Ref="ut2014_work";
                ID=f8370086-654e-4807-b79b-3f51653c3699
                OrderInList = 0
                Folder=/Рабочие
                OrderInTree = 0
                External=1
                ClientConnectionSpeed=Normal
                App = Auto
                WA=1
                Version=8.2
                DefaultApp=ThickClient
                -------------------- */
                if (line.StartsWith("[")) // Start
                {
                    name = line.Substring(1, line.Length - 2);
                    id = "";
                    path = "";
                }
                else if (line.StartsWith("Connect=")) // Connect string
                {
                    path = line.Substring(8);
                }
                else if (line.StartsWith("ID=")) // ID string
                {
                    id = line.Substring(3);
                }
                if (name != "" && path != "" && id != "")
                {
                    bases_dict.Add(id, (name, path));
                    name = "";
                    id = "";
                    path = "";
                }
            }
            return bases_dict;
        }
        private string[] ReadFileContent(string path_to_file)
        {
            string full_path_to_file = Environment.ExpandEnvironmentVariables(path_to_file);
            if (!File.Exists(full_path_to_file))
            {
                return null;
            }
            return System.IO.File.ReadAllLines(full_path_to_file);
        }
        private static long DirSize(DirectoryInfo d)
        {
            long size = 0;
            // Add file sizes.
            FileInfo[] fis = d.GetFiles();
            foreach (FileInfo fi in fis)
            {
                size += fi.Length;
            }
            // Add subdirectory sizes.
            DirectoryInfo[] dis = d.GetDirectories();
            foreach (DirectoryInfo di in dis)
            {
                size += DirSize(di);
            }
            return size;
        }
        private void SetToolTips()
        {
            ToolTip toolTip = new ToolTip();

            string ClearCache_ToolTip = "Для выбранных баз - удаляет только файлы кэша.\r\nПользовательские настройки сохраняются.";
            string PurgeAll_ToolTip = "Для выбранных баз - удаляет все найденные в корневых каталогах файлы.\r\nПользовательские настройки будут удалены.";
            string SmartSelect_ToolTip = "Выбрать только базы, в которых есть локальный кэш.";
            string SelectAll_ToolTip = "Выбрать все базы в списке.";
            string RemoveSelection_ToolTip = "Убрать выбор со всех баз в списке.";
            string About_ToolTip = "О программе";

            toolTip.SetToolTip(btnClearCache, ClearCache_ToolTip);
            toolTip.SetToolTip(btnPurgeAll, PurgeAll_ToolTip);
            toolTip.SetToolTip(btnSmartSelect, SmartSelect_ToolTip);
            toolTip.SetToolTip(btnSelectAll, SelectAll_ToolTip);
            toolTip.SetToolTip(btnRemoveSelection, RemoveSelection_ToolTip);
            toolTip.SetToolTip(btnAbout, About_ToolTip);
        }
    }
}