public void UpdateSelectedMaidValues()
        {
            if (InvokeRequired)
            {
                InvokeAsync((Action)UpdateSelectedMaidValues);
                return;
            }
            MaidChangeType cType = 0;

            Debugger.Assert(
                () =>
            {
                int processingQueue = currentQueue;
                currentQueue        = 1 - currentQueue;
                if (valueUpdateQueue[processingQueue].Count <= 0)
                {
                    return;
                }
                Debugger.WriteLine(LogLevel.Info, $"Updating values (Queue {processingQueue})...");
                foreach (KeyValuePair <MaidChangeType, Action> type in valueUpdateQueue[processingQueue])
                {
                    cType = type.Key;
                    type.Value();
                }
                valueUpdateQueue[processingQueue].Clear();
            },
                $"Failed to update scheduled maid value. Type: {cType}");
        }
        private void LoadTranslations(string selectedLanguageFile)
        {
            listBox_translations.Items.Clear();
            Debugger.WriteLine(
                LogLevel.Info,
                $"Loading translation files. Selected language file: {selectedLanguageFile}");
            string translationsPath = Path.Combine(MaidFiddler.DATA_PATH, Translation.TRANSLATIONS_PATH);

            if (!Directory.Exists(translationsPath))
            {
                Debugger.WriteLine(LogLevel.Warning, "No translation folder found. Creating one...");
                Directory.CreateDirectory(translationsPath);
                return;
            }

            string[] files    = Directory.GetFiles(translationsPath, "*.txt");
            int      selected = -1;

            foreach (string filePath in files)
            {
                using (StreamReader sr = File.OpenText(filePath))
                {
                    TranslationData translationData = new TranslationData();
                    string          line            = sr.ReadLine();

                    if (line == null || line.Trim() == string.Empty)
                    {
                        continue;
                    }

                    Match match = Translation.TagPattern.Match(line);
                    if (!match.Success)
                    {
                        continue;
                    }

                    translationData.FileName = Path.GetFileNameWithoutExtension(filePath);
                    translationData.Language = match.Groups["lang"].Value;
                    translationData.Version  = match.Groups["ver"].Value;
                    translationData.Author   = match.Groups["auth"].Value;

                    Debugger.WriteLine(
                        LogLevel.Info,
                        $"Found language: File={translationData.FileName}, Lang={translationData.Language}");

                    int i = listBox_translations.Items.Add(translationData);
                    if (translationData.FileName == selectedLanguageFile)
                    {
                        selected = i;
                    }
                }
            }

            if (selected != -1)
            {
                listBox_translations.SelectedIndex = selected;
            }
        }
        private void OpenTranslationsFolder(object sender, EventArgs e)
        {
            string translationsPath = Path.Combine(MaidFiddler.DATA_PATH, Translation.TRANSLATIONS_PATH);

            Debugger.WriteLine(LogLevel.Info, $"Opening translation folder at {translationsPath}");
            if (!Directory.Exists(translationsPath))
            {
                Debugger.WriteLine(LogLevel.Warning, "No translation folder found. Creating one...");
                Directory.CreateDirectory(translationsPath);
            }
            Process.Start(new ProcessStartInfo {
                FileName = translationsPath, UseShellExecute = true, Verb = "open"
            });
        }
 private void _UnloadMaids()
 {
     Debugger.Assert(
         () =>
     {
         Debugger.WriteLine(LogLevel.Info, "Unloading maids!");
         Debugger.WriteLine(LogLevel.Info, $"Loaded maids: {loadedMaids.Count}");
         loadedMaids.Clear();
         currentQueue = 0;
         valueUpdateQueue[0].Clear();
         valueUpdateQueue[1].Clear();
         if (maidThumbnails.Count > 0)
         {
             maidThumbnails.ForEach(thumb => thumb.Value.Dispose());
         }
         maidThumbnails.Clear();
         UpdateList();
     },
         "Failed to unload maids");
 }
        private void ReloadMaids(List <Maid> maids)
        {
            Debugger.Assert(
                () =>
            {
                Debugger.WriteLine(LogLevel.Info, "Reloading maids!");
                Debugger.WriteLine(LogLevel.Info, $"Maids: {maids.Count}");
                Debugger.WriteLine(LogLevel.Info, $"Loaded maids: {loadedMaids.Count}");
                loadedMaids.Clear();
                currentQueue = 0;
                valueUpdateQueue[0].Clear();
                valueUpdateQueue[1].Clear();
                if (maidThumbnails.Count > 0)
                {
                    maidThumbnails.ForEach(thumb => thumb.Value.Dispose());
                }
                maidThumbnails.Clear();
                loadedMaids = new SortedList <Maid, MaidInfo>(
                    maids.ToDictionary(m => m, m => new MaidInfo(m, this)),
                    comparer);
                loadedMaids.ForEach(
                    m =>
                {
                    Texture2D thumb = m.Value.Maid.GetThumIcon();
                    if (thumb == null)
                    {
                        return;
                    }
                    using (MemoryStream stream = new MemoryStream(thumb.EncodeToPNG()))
                    {
                        Debugger.WriteLine("Loading PNG of size: " + stream.Length);
                        maidThumbnails.Add(m.Key.Param.status.guid, Image.FromStream(stream));
                    }
                });

                UpdateList();
            },
                "Failed to reload all maids");
        }
        private void UpdateMaids(List <Maid> newMaids)
        {
            Debugger.Assert(
                () =>
            {
                if (newMaids.Count != loadedMaids.Count)
                {
                    goto update;
                }

                newMaids.Sort((m1, m2) => comparer.Compare(m1, m2));
                if (newMaids.SequenceEqual(loadedMaids.Values.Select(m => m.Maid), comparer))
                {
                    return;
                }

                update:
#if DEBUG
                Stopwatch sw = Stopwatch.StartNew();
#endif
                Debugger.WriteLine(LogLevel.Info, "Updating maid list!");
                Debugger.WriteLine(LogLevel.Info, $" New count:  {newMaids.Count}, Loaded count: {loadedMaids.Count}");
                loadedMaids = new SortedList <Maid, MaidInfo>(
                    loadedMaids.Where(
                        m =>
                {
                    bool result = newMaids.Contains(m.Key);
                    if (result)
                    {
                        newMaids.Remove(m.Key);
                    }
                    else
                    {
                        if (SelectedMaid != null && m.Value.Maid == SelectedMaid.Maid)
                        {
                            currentQueue = 0;
                            valueUpdateQueue[0].Clear();
                            valueUpdateQueue[1].Clear();
                        }
                        if (maidThumbnails.ContainsKey(m.Key.Param.status.guid))
                        {
                            maidThumbnails[m.Key.Param.status.guid].Dispose();
                        }
                        maidThumbnails.Remove(m.Key.Param.status.guid);
                    }
                    return(result);
                }).ToList().Union(
                        newMaids.Select(
                            m =>
                {
                    Debugger.WriteLine(LogLevel.Info, "Adding new maid info.");
                    MaidInfo info = new MaidInfo(m, this);
                    Debugger.WriteLine(LogLevel.Info, "Loading thumbnail");
                    Texture2D thumb = m.GetThumIcon();
                    if (thumb == null)
                    {
                        return(new KeyValuePair <Maid, MaidInfo>(m, info));
                    }
                    using (MemoryStream stream = new MemoryStream(thumb.EncodeToPNG()))
                    {
                        Debugger.WriteLine("Loading PNG of size: " + stream.Length);
                        maidThumbnails.Add(m.Param.status.guid, Image.FromStream(stream));
                    }
                    return(new KeyValuePair <Maid, MaidInfo>(m, info));
                })).ToDictionary(m => m.Key, m => m.Value),
                    comparer);


                Debugger.WriteLine(LogLevel.Info, $"New loaded maids count: {loadedMaids.Count}");
#if DEBUG
                sw.Stop();
                Debugger.WriteLine(LogLevel.Info, $"Updated maid list in {sw.Elapsed.TotalMilliseconds} ms");

                newMaids.ForEach(
                    m => Debugger.WriteLine($"Added {m.Param.status.first_name} {m.Param.status.last_name}"));
                Debugger.WriteLine();
#endif
                Debugger.WriteLine("Updating list.");
                UpdateList();
                Debugger.WriteLine("Finished updating list.");
            },
                "Failed to update maid list");
        }
        private void OpenTranslationDownloadUrl(object sender, EventArgs e)
        {
            Uri        uri   = null;
            TextDialog tdURL = new TextDialog(
                Translation.GetTranslation("TL_URL_TITLE"),
                Translation.GetTranslation("TL_URL_TEXT"),
                string.Empty,
                s =>
            {
                try
                {
                    uri = new Uri(s);
                    return(uri.Scheme == Uri.UriSchemeHttp || uri.Scheme == Uri.UriSchemeHttps);
                }
                catch (Exception)
                {
                    return(false);
                }
            },
                Translation.GetTranslation("OK"),
                Translation.GetTranslation("CANCEL"));
            DialogResult promptDialog = tdURL.ShowDialog(this);

            tdURL.Dispose();

            if (promptDialog != DialogResult.OK)
            {
                return;
            }

            string        translationsPath = Path.Combine(MaidFiddler.DATA_PATH, Translation.TRANSLATIONS_PATH);
            LoadingBarGUI loadingBarGui    = new LoadingBarGUI(
                Translation.GetTranslation("LOADING"),
                Translation.GetTranslation("TL_URL_DOWNLOADING"),
                true,
                g =>
            {
                HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(uri);
                g.Timer.Start();
                Debugger.WriteLine(LogLevel.Info, "Getting translation...");
                webRequest.BeginGetResponse(
                    ar =>
                {
                    try
                    {
                        HttpWebResponse response = (HttpWebResponse)webRequest.EndGetResponse(ar);
                        Debugger.WriteLine(LogLevel.Info, "Got response!");
                        Debugger.WriteLine(LogLevel.Info, $"Response: {response.StatusCode}");
                        if (response.StatusCode == HttpStatusCode.NotFound)
                        {
                            MessageBox.Show(
                                "Failed to download the translation: File not found",
                                "Boop!",
                                MessageBoxButtons.OK,
                                MessageBoxIcon.Error);
                            g.DialogResult = DialogResult.Abort;
                            g.Timer.Stop();
                            g.Close();
                            return;
                        }
                        Stream s = response.GetResponseStream();
                        Debugger.WriteLine(LogLevel.Info, "Reading response");
                        List <byte> resultBuffer = new List <byte>();
                        byte[] responseBuffer    = new byte[1024];
                        int read;
                        do
                        {
                            read = s.Read(responseBuffer, 0, responseBuffer.Length);
                            resultBuffer.AddRange(responseBuffer, 0, read);
                        } while (read > 0);
                        string translationString = Encoding.UTF8.GetString(resultBuffer.ToArray());

                        using (TextReader tr = new StringReader(translationString))
                        {
                            if (!Translation.TagPattern.Match(tr.ReadLine()).Success)
                            {
                                Debugger.WriteLine(LogLevel.Error, "Failed to parse the translation: No tag found!");
                                MessageBox.Show(
                                    "The file does not contain the translation tag and thus cannot be recognised as a Maid Fiddler translation file.\nThe file has not been saved.",
                                    "Boop!",
                                    MessageBoxButtons.OK,
                                    MessageBoxIcon.Error);
                                g.DialogResult = DialogResult.Abort;
                                g.Timer.Stop();
                                g.Close();
                                return;
                            }
                        }

                        string tlFileName = Path.GetFileNameWithoutExtension(uri.AbsolutePath);
                        Debugger.WriteLine($"File name: {tlFileName}");

                        if (tlFileName == string.Empty ||
                            File.Exists(Path.Combine(translationsPath, $"{tlFileName}.txt")))
                        {
                            char[] invalidChars   = Path.GetInvalidFileNameChars();
                            TextDialog tdFileName = new TextDialog(
                                Translation.GetTranslation("TL_NAME_CHANGE_TITLE"),
                                Translation.GetTranslation("TL_NAME_CHANGE"),
                                tlFileName,
                                s1 => !s1.Any(c => invalidChars.Contains(c)),
                                Translation.GetTranslation("OK"),
                                Translation.GetTranslation("CANCEL"));
                            if (tdFileName.ShowDialog(g) == DialogResult.OK)
                            {
                                tlFileName = tdFileName.Input;
                            }
                            if (tlFileName == string.Empty)
                            {
                                tlFileName = FiddlerUtils.GenerateFileName();
                            }

                            tdFileName.Dispose();
                        }

                        string path = Path.Combine(translationsPath, $"{tlFileName}.txt");
                        Debugger.WriteLine($"Writing translation to {path}");

                        using (TextWriter tw = File.CreateText(path))
                            tw.Write(translationString);
                        g.DialogResult = DialogResult.OK;
                    }
                    catch (WebException we)
                    {
                        MessageBox.Show(
                            $"Failed to retreive translation.\nResponse: {we.Message}",
                            "Boop!",
                            MessageBoxButtons.OK,
                            MessageBoxIcon.Error);
                        g.DialogResult = DialogResult.Abort;
                    }
                    catch (Exception ex)
                    {
                        MessageBox.Show(
                            $"Unknown error occurred.\nInfo: {ex.ToString()}",
                            "Boop!",
                            MessageBoxButtons.OK,
                            MessageBoxIcon.Error);
                        g.DialogResult = DialogResult.Abort;
                    }
                    finally
                    {
                        g.Timer.Stop();
                        g.Close();
                    }
                },
                    null);
            });
            DialogResult result = loadingBarGui.ShowDialog(this);

            loadingBarGui.Dispose();
            if (result != DialogResult.OK)
            {
                return;
            }
            MessageBox.Show(
                Translation.GetTranslation("TL_DOWNLOAD_DONE"),
                Translation.GetTranslation("TL_DOWNLOAD_DONE_TITLE"),
                MessageBoxButtons.OK);
            LoadTranslations(Translation.CurrentTranslationFile);
        }
        private void OpenTranslationDownloadGithub(object sender, EventArgs e)
        {
            string        list          = string.Empty;
            LoadingBarGUI loadingBarGui = new LoadingBarGUI(
                Translation.GetTranslation("LOADING"),
                Translation.GetTranslation("TL_LIST_LOADING"),
                true,
                g =>
            {
                HttpWebRequest webRequest =
                    (HttpWebRequest)
                    WebRequest.Create($"{MaidFiddler.RESOURCE_URL}/Resources/Translations/translation_list.txt");
                g.Timer.Start();
                Debugger.WriteLine(LogLevel.Info, "Getting translation list...");
                webRequest.BeginGetResponse(
                    ar =>
                {
                    try
                    {
                        HttpWebResponse response = (HttpWebResponse)webRequest.EndGetResponse(ar);
                        Debugger.WriteLine(LogLevel.Info, "Got response!");
                        Debugger.WriteLine(LogLevel.Info, $"Response: {response.StatusCode}");
                        if (response.StatusCode == HttpStatusCode.NotFound)
                        {
                            MessageBox.Show(
                                "Failed to retreive translation list: List not found.",
                                "Boop!",
                                MessageBoxButtons.OK,
                                MessageBoxIcon.Error);
                        }
                        Stream s = response.GetResponseStream();
                        Debugger.WriteLine(LogLevel.Info, "Reading response");
                        byte[] responseBuffer  = new byte[1024];
                        List <byte> byteBuffer = new List <byte>();
                        int read;
                        do
                        {
                            read = s.Read(responseBuffer, 0, responseBuffer.Length);
                            byteBuffer.AddRange(responseBuffer, 0, read);
                        } while (read > 0);
                        list           = Encoding.UTF8.GetString(byteBuffer.ToArray());
                        g.DialogResult = DialogResult.OK;
                    }
                    catch (WebException we)
                    {
                        MessageBox.Show(
                            $"Failed to retreive translation list.\nResponse: {we.Message}",
                            "Boop!",
                            MessageBoxButtons.OK,
                            MessageBoxIcon.Error);
                        g.DialogResult = DialogResult.Abort;
                    }
                    catch (Exception ex)
                    {
                        MessageBox.Show(
                            $"Unknown error occurred.\nInfo: {ex.ToString()}",
                            "Boop!",
                            MessageBoxButtons.OK,
                            MessageBoxIcon.Error);
                        g.DialogResult = DialogResult.Abort;
                    }
                    finally
                    {
                        g.Timer.Stop();
                        g.Close();
                    }
                },
                    null);
            });
            DialogResult result = loadingBarGui.ShowDialog(this);

            loadingBarGui.Dispose();
            if (result != DialogResult.OK)
            {
                return;
            }
            GithubTranslationsGUI tlGui = new GithubTranslationsGUI(list.Remove(0, 1));

            tlGui.ShowDialog(this);
            tlGui.Dispose();
            LoadTranslations(Translation.CurrentTranslationFile);
        }