Korero – Rev 2

Subversion Repositories:
Rev:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using Korero.Communication;
using Korero.Database;
using Korero.Economy;
using Korero.Profile;
using Korero.Properties;
using Korero.Serialization;
using Korero.Utilities;
using Serilog;

namespace Korero.Chat
{
    public partial class ChatForm : Form
    {
        #region Private Delegates, Events, Enums, Properties, Indexers and Fields

        private readonly CancellationToken _cancellationToken;

        private readonly CancellationTokenSource _cancellationTokenSource;

        private readonly Task _loadMessagesTask;

        private readonly MainForm _mainForm;

        private readonly MqttCommunication _mqttCommunication;

        private AutoResponseForm _autoResponseForm;

        private AvatarProfileForm _avatarProfileForm;

        private GroupProfileForm _groupProfileForm;

        private NewConversationForm _newConversationForm;

        private PaymentForm _paymentForm;

        #endregion

        #region Constructors, Destructors and Finalizers

        public ChatForm()
        {
            InitializeComponent();

            _cancellationTokenSource = new CancellationTokenSource();
            _cancellationToken = _cancellationTokenSource.Token;

            chatListBox.SetDoubleBuffered();
        }

        public ChatForm(MainForm mainForm, MqttCommunication mqttCommunication) : this()
        {
            _mainForm = mainForm;
            _mqttCommunication = mqttCommunication;

            _loadMessagesTask = LoadUnseenMessages(_mainForm.MessageDatabase);

            _mqttCommunication.NotificationReceived += MqttCommunication_NotificationReceived;
        }

        /// <summary>
        ///     Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && components != null)
            {
                components.Dispose();
            }

            _mqttCommunication.NotificationReceived -= MqttCommunication_NotificationReceived;

            base.Dispose(disposing);
        }

        #endregion

        #region Event Handlers
        private void ChatForm_Load(object sender, EventArgs e)
        {
            Utilities.WindowState.FormTracker.Track(this);
        }

        private async void MqttCommunication_NotificationReceived(object sender, MqttNotificationEventArgs e)
        {
            switch (e.Notification["notification"])
            {
                case "group":
                    var databaseMessageGroup = new DatabaseMessageGroup(e.Notification["firstname"],
                        e.Notification["lastname"], e.Notification["message"], e.Notification["group"], DateTime.Now);

                    var messageConversationGroup = new ConversationGroup(databaseMessageGroup);

                    chatListBox.InvokeIfRequired(listBox =>
                    {
                        var item = listBox.Items.OfType<Conversation>()
                            .FirstOrDefault(conversation => conversation.Name == messageConversationGroup.Name);
                        switch (item == null)
                        {
                            case true:
                                listBox.Items.Add(messageConversationGroup);
                                messageConversationGroup.Seen = false;
                                listBox.Refresh();
                                break;
                            default:
                                // If the conversation is not selected then highlight it.
                                if (listBox.SelectedItem != null)
                                {
                                    if (((Conversation) listBox.SelectedItem).Name != item.Name)
                                    {
                                        item.Seen = false;
                                        listBox.Refresh();
                                    }
                                }

                                break;
                        }

                        var index = listBox.SelectedIndex;
                        if (index == -1)
                        {
                            return;
                        }

                        if (((Conversation) listBox.Items[index]).Name != messageConversationGroup.Name)
                        {
                            return;
                        }

                        chatTextBox.InvokeIfRequired(textBox =>
                        {
                            textBox.AppendText(
                                $"{databaseMessageGroup.FirstName} {databaseMessageGroup.LastName}: {databaseMessageGroup.Message}" +
                                Environment.NewLine);
                        });
                    });
                    break;
                case "message":
                    var databaseMessage = new DatabaseMessage(e.Notification["firstname"], e.Notification["lastname"],
                        e.Notification["message"], DateTime.Now);
                    var messageConversation = new ConversationAvatar(databaseMessage);

                    chatListBox.InvokeIfRequired(listBox =>
                    {
                        var item = listBox.Items.OfType<Conversation>()
                            .FirstOrDefault(conversation => conversation.Name == messageConversation.Name);

                        switch (item == null)
                        {
                            case true:
                                listBox.Items.Add(messageConversation);
                                messageConversation.Seen = false;
                                listBox.Refresh();
                                break;
                            default:
                                // If the conversation is not selected then highlight it.
                                if (listBox.SelectedItem != null)
                                {
                                    if (((Conversation) listBox.SelectedItem).Name != item.Name)
                                    {
                                        item.Seen = false;
                                        listBox.Refresh();
                                    }
                                }

                                break;
                        }

                        var index = listBox.SelectedIndex;
                        if (index == -1)
                        {
                            return;
                        }

                        if (((Conversation) listBox.Items[index]).Name != messageConversation.Name)
                        {
                            return;
                        }

                        chatTextBox.InvokeIfRequired(textBox =>
                        {
                            textBox.AppendText(
                                $"{databaseMessage.FirstName} {databaseMessage.LastName}: {databaseMessage.Message}" +
                                Environment.NewLine);
                        });
                    });

                    // Send away message if the current status is set to away.
                    if (Settings.Default.CurrentStatus == Resources.Away)
                    {
                        var data = new Dictionary<string, string>
                        {
                            {"command", "tell"},
                            {"group", Settings.Default.Group},
                            {"password", Settings.Default.Password},
                            {"entity", "avatar"},
                            {"firstname", messageConversation.FirstName},
                            {"lastname", messageConversation.LastName},
                            {"message", Settings.Default.AutoResponseText}
                        };

                        var callback =
                            await _mqttCommunication.SendCommand(new Command(data), _cancellationToken);

                        if (callback == null || !callback.Success)
                        {
                            if (callback != null)
                            {
                                Log.Warning("Command {Command} has failed with {Error}.",
                                    callback.Command, callback.Error);
                            }

                            break;
                        }

                        var databaseSelfMessage = new DatabaseMessage(messageConversation.FirstName,
                            messageConversation.LastName, Settings.Default.AutoResponseText, DateTime.Now);

                        await _mainForm.MessageDatabase.SaveSelfMessage(databaseSelfMessage);

                        chatListBox.InvokeIfRequired(async listBox =>
                        {
                            var item = listBox.Items.OfType<Conversation>()
                                .FirstOrDefault(conversation => conversation.Name == messageConversation.Name);

                            switch (item == null)
                            {
                                case true:
                                    listBox.Items.Add(messageConversation);
                                    messageConversation.Seen = false;
                                    listBox.Refresh();
                                    break;
                                default:
                                    // If the conversation is not selected then highlight it.
                                    if (listBox.SelectedItem != null)
                                    {
                                        switch (((Conversation) listBox.SelectedItem).Name != item.Name)
                                        {
                                            case true:
                                                item.Seen = false;
                                                listBox.Refresh();

                                                break;
                                            default:
                                                data = new Dictionary<string, string>
                                                {
                                                    {"command", "getselfdata"},
                                                    {"group", Settings.Default.Group},
                                                    {"password", Settings.Default.Password},
                                                    {"data", new CSV(new[] {"FirstName", "LastName"})}
                                                };

                                                callback = await _mqttCommunication.SendCommand(
                                                    new Command(data),
                                                    _cancellationToken);

                                                if (callback == null || !callback.Success)
                                                {
                                                    if (callback != null)
                                                    {
                                                        Log.Warning("Command {Command} has failed with {Error}.",
                                                            "getselfdata", callback.Error);
                                                    }

                                                    break;
                                                }

                                                chatTextBox.InvokeIfRequired(textBox =>
                                                {
                                                    textBox.AppendText(
                                                        $"{callback["FirstName"].FirstOrDefault()} {callback["LastName"].FirstOrDefault()}: {databaseSelfMessage.Message}" +
                                                        Environment.NewLine);
                                                });
                                                break;
                                        }
                                    }

                                    break;
                            }
                        });
                    }

                    break;
            }
        }

        private async void ListBox1_SelectedIndexChanged(object sender, EventArgs e)
        {
            var listBox = (ListBox) sender;

            var index = listBox.SelectedIndex;
            if (index == -1)
            {
                return;
            }

            var item = listBox.Items[index];
            if (item == null)
            {
                return;
            }

            // Mark the conversation as seen.
            ((Conversation) item).Seen = true;
            await _mainForm.MessageDatabase.MarkConversationSeen($"{((Conversation) item).Name}", true);
            chatListBox.Refresh();

            chatTextBox.Clear();
            switch (item)
            {
                case ConversationAvatar conversationAvatar:
                    var messages = await _mainForm.MessageDatabase.LoadMessages(conversationAvatar.FirstName,
                        conversationAvatar.LastName,
                        (int) Settings.Default.ChatMessageHistoryLength);
                    var selfMessages =
                        await _mainForm.MessageDatabase.LoadSelfMessages(conversationAvatar.FirstName,
                            conversationAvatar.LastName,
                            (int) Settings.Default.ChatMessageHistoryLength);

                    var data = new Dictionary<string, string>
                    {
                        {"command", "getselfdata"},
                        {"group", Settings.Default.Group},
                        {"password", Settings.Default.Password},
                        {"data", new CSV(new[] {"FirstName", "LastName"})}
                    };

                    var callback = await _mqttCommunication.SendCommand(new Command(data), _cancellationToken);

                    if (callback == null || !callback.Success)
                    {
                        if (callback != null)
                        {
                            Log.Warning("Command {Command} has failed with {Error}.",
                                callback.Command, callback.Error);
                        }

                        break;
                    }

                    var selfDatabaseMessage = selfMessages.Select(selfMessage =>
                        new DatabaseMessage(callback["FirstName"].FirstOrDefault(),
                            callback["LastName"].FirstOrDefault(), selfMessage.Message,
                            selfMessage.Time)).ToList();

                    var databaseMessages = messages.Concat(selfDatabaseMessage).ToList();
                    databaseMessages.Sort(new DatabaseMessageTimeComparer());

                    foreach (var message in databaseMessages)
                    {
                        chatTextBox.AppendText($"{message.FirstName} {message.LastName}: {message.Message}" +
                                               Environment.NewLine);
                    }

                    break;
                case ConversationGroup groupConversation:
                    chatTextBox.InvokeIfRequired(textBox => { textBox.Clear(); });

                    var groupMessages = await _mainForm.MessageDatabase.LoadMessagesGroup(groupConversation.Name,
                        (int) Settings.Default.ChatMessageHistoryLength);
                    var groupDatabaseMessages = groupMessages.ToList();
                    groupDatabaseMessages.Sort(new DatabaseMessageTimeComparer());

                    foreach (var message in groupDatabaseMessages)
                    {
                        chatTextBox.AppendText($"{message.FirstName} {message.LastName}: {message.Message}" +
                                               Environment.NewLine);
                    }

                    break;
            }
        }

        private async void Button1_Click(object sender, EventArgs e)
        {
            await SendMessage();
        }

        private async void TextBox1_KeyPress(object sender, KeyPressEventArgs e)
        {
            if (e.KeyChar != (char) Keys.Enter)
            {
                return;
            }

            await SendMessage();
        }

        private void NewToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (_newConversationForm != null)
            {
                return;
            }

            _newConversationForm = new NewConversationForm(_mainForm.MqttCommunication);
            _newConversationForm.NewConversationSelected += AvatarFormAvatarSelected;
            _newConversationForm.Closing += NewConversationForm_Closing;
            _newConversationForm.Show();
        }

        private async void AvatarFormAvatarSelected(object sender, NewConversationSelectedEventArgs e)
        {
            switch (e.Conversation)
            {
                case ConversationAvatar newConversationAvatar:
                    var messageConversation =
                        new ConversationAvatar(newConversationAvatar.FirstName, newConversationAvatar.LastName);
                    await _mainForm.MessageDatabase.MarkConversationSeen(
                        $"{newConversationAvatar.FirstName} {newConversationAvatar.LastName}", true);

                    chatListBox.InvokeIfRequired(listBox =>
                    {
                        if (!listBox.Items.OfType<ConversationAvatar>().Contains(messageConversation))
                        {
                            listBox.Items.Add(messageConversation);
                        }

                        listBox.SelectedItem = messageConversation;
                    });
                    break;
                case ConversationGroup newConversationGroup:
                    var messageConversationGroup = new ConversationGroup(newConversationGroup.Name);
                    await _mainForm.MessageDatabase.MarkConversationSeen($"{newConversationGroup.Name}", true);

                    chatListBox.InvokeIfRequired(listBox =>
                    {
                        if (!listBox.Items.OfType<ConversationGroup>().Contains(messageConversationGroup))
                        {
                            listBox.Items.Add(messageConversationGroup);
                        }

                        listBox.SelectedItem = messageConversationGroup;
                    });
                    break;
            }
        }

        private void NewConversationForm_Closing(object sender, CancelEventArgs e)
        {
            if (_newConversationForm == null)
            {
                return;
            }

            _newConversationForm.NewConversationSelected -= AvatarFormAvatarSelected;
            _newConversationForm.Closing -= NewConversationForm_Closing;
            _newConversationForm.Dispose();
            _newConversationForm = null;
        }

        private void SplitContainer1_Paint(object sender, PaintEventArgs e)
        {
            var control = (SplitContainer) sender;
            // paint the three dots
            var points = new Point[3];
            var w = control.Width;
            var h = control.Height;
            var d = control.SplitterDistance;
            var sW = control.SplitterWidth;

            //calculate the position of the points
            if (control.Orientation == Orientation.Horizontal)
            {
                points[0] = new Point(w / 2, d + sW / 2);
                points[1] = new Point(points[0].X - 10, points[0].Y);
                points[2] = new Point(points[0].X + 10, points[0].Y);
            }
            else
            {
                points[0] = new Point(d + sW / 2, h / 2);
                points[1] = new Point(points[0].X, points[0].Y - 10);
                points[2] = new Point(points[0].X, points[0].Y + 10);
            }

            foreach (var p in points)
            {
                p.Offset(-2, -2);
                e.Graphics.FillEllipse(SystemBrushes.ControlDark,
                    new Rectangle(p, new Size(3, 3)));

                p.Offset(1, 1);
                e.Graphics.FillEllipse(SystemBrushes.ControlLight,
                    new Rectangle(p, new Size(3, 3)));
            }
        }

        private void CloseToolStripMenuItem_Click(object sender, EventArgs e)
        {
            var index = chatListBox.SelectedIndex;
            if (index == -1)
            {
                return;
            }

            var selectedItem = chatListBox.SelectedItem;
            chatListBox.ClearSelected();
            chatListBox.Items.Remove(selectedItem);
            chatTextBox.Clear();
        }

        private async void OpenAllToolStripMenuItem_Click(object sender, EventArgs e)
        {
            await LoadAllMessages(_mainForm.MessageDatabase);
        }

        private void CloseAllToolStripMenuItem1_Click(object sender, EventArgs e)
        {
            chatListBox.Items.Clear();
            chatTextBox.Clear();
        }

        private void ChatListBox_DrawItem(object sender, DrawItemEventArgs e)
        {
            var listBox = (ListBox) sender;

            if (e.Index == -1)
            {
                return;
            }

            var item = (Conversation) listBox.Items[e.Index];

            if (item == null)
            {
                return;
            }

            if ((e.State & DrawItemState.Focus) == DrawItemState.Focus)
            {
                e.Graphics.FillRectangle(new SolidBrush(SystemColors.Highlight), e.Bounds);
                if (item.Seen)
                {
                    e.Graphics.DrawString(item.Name, e.Font, new SolidBrush(SystemColors.HighlightText), e.Bounds);
                    return;
                }

                e.Graphics.DrawString(item.Name, e.Font, new SolidBrush(Settings.Default.SeenMessageColor), e.Bounds);

                return;
            }

            e.Graphics.FillRectangle(new SolidBrush(chatListBox.BackColor), e.Bounds);
            if (item.Seen)
            {
                e.Graphics.DrawString(item.Name, e.Font, new SolidBrush(chatListBox.ForeColor), e.Bounds);
                return;
            }

            e.Graphics.DrawString(item.Name, e.Font, new SolidBrush(Settings.Default.SeenMessageColor), e.Bounds);
        }

        private void SearchTextBox_TextChanged(object sender, EventArgs e)
        {
            if (string.IsNullOrEmpty(searchTextBox.Text))
            {
                chatTextBox.SelectAll();
                chatTextBox.SelectionBackColor = searchTextBox.BackColor;
                chatTextBox.SelectionStart = chatTextBox.Text.Length;
                chatTextBox.ScrollToCaret();
                return;
            }

            Regex regex;
            try
            {
                regex = new Regex(searchTextBox.Text);
            }
            catch
            {
                chatTextBox.SelectAll();
                chatTextBox.SelectionBackColor = searchTextBox.BackColor;
                chatTextBox.SelectionStart = chatTextBox.Text.Length;
                chatTextBox.ScrollToCaret();
                return;
            }

            var matches = regex.Matches(chatTextBox.Text);
            chatTextBox.SelectAll();
            chatTextBox.SelectionBackColor = searchTextBox.BackColor;
            foreach (Match match in matches)
            {
                chatTextBox.Select(match.Index, match.Length);
                chatTextBox.SelectionBackColor = Settings.Default.ChatSearchHighlight;
            }
        }

        private void ChatTextBox_TextChanged(object sender, EventArgs e)
        {
            var richTextBox = (RichTextBox) sender;

            richTextBox.ScrollToCaret();
        }

        private void ProfileToolStripMenuItem_Click(object sender, EventArgs e)
        {
            var index = chatListBox.SelectedIndex;
            if (index == -1)
            {
                return;
            }

            var selectedItem = chatListBox.SelectedItem;

            switch (selectedItem)
            {
                case ConversationAvatar conversationAvatar:
                    if (_avatarProfileForm != null)
                    {
                        return;
                    }

                    _avatarProfileForm = new AvatarProfileForm(_mainForm, conversationAvatar.FirstName,
                        conversationAvatar.LastName, _mqttCommunication);
                    _avatarProfileForm.Closing += AvatarProfileForm_Closing;
                    _avatarProfileForm.Show();
                    break;
                case ConversationGroup conversationGroup:
                    if (_groupProfileForm != null)
                    {
                        return;
                    }

                    _groupProfileForm = new GroupProfileForm(_mainForm, conversationGroup.Name, _mqttCommunication);
                    _groupProfileForm.Closing += GroupProfileForm_Closing;
                    _groupProfileForm.Show();
                    break;
            }
        }

        private void GroupProfileForm_Closing(object sender, CancelEventArgs e)
        {
            if (_groupProfileForm == null)
            {
                return;
            }

            _groupProfileForm.Closing -= GroupProfileForm_Closing;
            _groupProfileForm.Dispose();
            _groupProfileForm = null;
        }

        private void AvatarProfileForm_Closing(object sender, CancelEventArgs e)
        {
            if (_avatarProfileForm == null)
            {
                return;
            }

            _avatarProfileForm.Closing -= AvatarProfileForm_Closing;
            _avatarProfileForm.Dispose();
            _avatarProfileForm = null;
        }

        private async void AddToFriendsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            var index = chatListBox.SelectedIndex;
            if (index == -1)
            {
                return;
            }

            var selectedItem = chatListBox.SelectedItem;

            switch (selectedItem)
            {
                case ConversationAvatar conversationAvatar:
                    var data = new Dictionary<string, string>
                    {
                        {"command", "offerfriendship"},
                        {"group", Settings.Default.Group},
                        {"password", Settings.Default.Password},
                        {"firstname", conversationAvatar.FirstName},
                        {"lastname", conversationAvatar.LastName}
                    };

                    var callback = await _mqttCommunication.SendCommand(new Command(data), _cancellationToken);

                    if (callback == null || !callback.Success)
                    {
                        if (callback != null)
                        {
                            Log.Warning("Command {Command} has failed with {Error}.",
                                callback.Command, callback.Error);
                        }
                    }

                    break;
            }
        }

        private void ChatTextBox_LinkClicked(object sender, LinkClickedEventArgs e)
        {
            Process.Start(e.LinkText);
        }

        private void SetAutoResponseToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (_autoResponseForm != null)
            {
                return;
            }

            _autoResponseForm = new AutoResponseForm();
            _autoResponseForm.AutoResponseSet += AutoResponseForm_AutoResponseSet;
            _autoResponseForm.Closing += AutoResponseForm_Closing;
            _autoResponseForm.Show();
        }

        private void AutoResponseForm_AutoResponseSet(object sender, AutoResponseSetEventArgs e)
        {
        }

        private void AutoResponseForm_Closing(object sender, CancelEventArgs e)
        {
            if (_autoResponseForm == null)
            {
                return;
            }

            _autoResponseForm.AutoResponseSet -= AutoResponseForm_AutoResponseSet;
            _autoResponseForm.Closing -= AutoResponseForm_Closing;
            _autoResponseForm.Dispose();
            _autoResponseForm = null;
        }

        private void ToolStripComboBox2_SelectedIndexChanged(object sender, EventArgs e)
        {
            var toolStripComboBox = (ToolStripComboBox) sender;

            var index = toolStripComboBox.SelectedIndex;
            if (index == -1)
            {
                return;
            }

            var selectedItem = (string) toolStripComboBox.SelectedItem;

            Settings.Default.CurrentStatus = selectedItem;
        }

        private async void OfferTeleportToolStripMenuItem_Click(object sender, EventArgs e)
        {
            var index = chatListBox.SelectedIndex;
            if (index == -1)
            {
                return;
            }

            var selectedItem = (Conversation) chatListBox.SelectedItem;

            if (!(selectedItem is ConversationAvatar conversationAvatar))
            {
                return;
            }

            var data = new Dictionary<string, string>
            {
                {"command", "lure"},
                {"group", Settings.Default.Group},
                {"password", Settings.Default.Password},
                {"firstname", conversationAvatar.FirstName},
                {"lastname", conversationAvatar.LastName}
            };

            var callback = await _mqttCommunication.SendCommand(new Command(data), _cancellationToken);

            if (callback == null || !callback.Success)
            {
                if (callback != null)
                {
                    Log.Warning("Command {Command} has failed with {Error}.",
                        callback.Command, callback.Error);
                }
            }
        }

        public void ChatForm_FormClosing(object sender, EventArgs e)
        {
            _cancellationTokenSource.Cancel();
        }

        private void PayToolStripMenuItem_Click(object sender, EventArgs e)
        {
            var index = chatListBox.SelectedIndex;
            if (index == -1)
            {
                return;
            }

            switch (chatListBox.SelectedItem)
            {
                case ConversationAvatar conversationAvatar:
                    if (_paymentForm != null)
                    {
                        return;
                    }

                    _paymentForm = new PaymentForm(conversationAvatar.FirstName, conversationAvatar.LastName, _mainForm,
                        _mqttCommunication);
                    _paymentForm.Closing += PaymentForm_Closing;
                    _paymentForm.Show();
                    break;
                case ConversationGroup conversationGroup:
                    if (_paymentForm != null)
                    {
                        return;
                    }

                    _paymentForm = new PaymentForm(conversationGroup.Name, _mainForm, _mqttCommunication);
                    _paymentForm.Closing += PaymentForm_Closing;
                    _paymentForm.Show();
                    break;
            }
        }

        private void PaymentForm_Closing(object sender, CancelEventArgs e)
        {
            if (_paymentForm == null)
            {
                return;
            }

            _paymentForm.Closing -= PaymentForm_Closing;
            _paymentForm.Dispose();
            _paymentForm = null;
        }

        #endregion

        #region Public Methods

        public void CreateOrSelect(string name)
        {
            var found = false;
            chatListBox.InvokeIfRequired(listBox =>
            {
                foreach (var item in listBox.Items.OfType<Conversation>())
                {
                    if (item.Name != name)
                    {
                        continue;
                    }

                    listBox.SelectedItem = item;
                    found = true;
                    break;
                }
            });


            if (found)
            {
                return;
            }

            var conversation = new ConversationAvatar(name);
            chatListBox.InvokeIfRequired(listBox =>
            {
                listBox.Items.Add(conversation);
                listBox.SelectedItem = conversation;
            });
        }

        #endregion

        #region Private Methods

        private async Task SendMessage()
        {
            var index = chatListBox.SelectedIndex;
            if (index == -1)
            {
                return;
            }

            var text = typeTextBox.Text;
            typeTextBox.InvokeIfRequired(textBox => { textBox.Clear(); });
            Dictionary<string, string> data;
            Callback callback;
            switch (chatListBox.Items[index])
            {
                case ConversationGroup conversationGroup:
                    data = new Dictionary<string, string>
                    {
                        {"command", "tell"},
                        {"group", Settings.Default.Group},
                        {"password", Settings.Default.Password},
                        {"entity", "group"},
                        {"target", conversationGroup.Name},
                        {"message", text}
                    };

                    callback = await _mqttCommunication.SendCommand(new Command(data), _cancellationToken);

                    if (callback == null || !callback.Success)
                    {
                        if (callback != null)
                        {
                            Log.Warning("Command {Command} has failed with {Error}.",
                                callback.Command, callback.Error);
                        }
                    }

                    break;
                case ConversationAvatar conversation:
                    data = new Dictionary<string, string>
                    {
                        {"command", "tell"},
                        {"group", Settings.Default.Group},
                        {"password", Settings.Default.Password},
                        {"entity", "avatar"},
                        {"firstname", conversation.FirstName},
                        {"lastname", conversation.LastName},
                        {"message", text}
                    };

                    callback = await _mqttCommunication.SendCommand(new Command(data), _cancellationToken);

                    if (callback == null || !callback.Success)
                    {
                        if (callback != null)
                        {
                            Log.Warning("Command {Command} has failed with {Error}.",
                                callback.Command, callback.Error);
                        }

                        break;
                    }

                    data = new Dictionary<string, string>
                    {
                        {"command", "getselfdata"},
                        {"group", Settings.Default.Group},
                        {"password", Settings.Default.Password},
                        {"data", new CSV(new[] {"FirstName", "LastName"})}
                    };

                    callback = await _mqttCommunication.SendCommand(new Command(data), _cancellationToken);

                    if (callback == null || !callback.Success)
                    {
                        if (callback != null)
                        {
                            Log.Warning("Command {Command} has failed with {Error}.",
                                callback.Command, callback.Error);
                        }

                        break;
                    }

                    await _mainForm.MessageDatabase.SaveSelfMessage(new DatabaseMessage(conversation.FirstName,
                        conversation.LastName, text, DateTime.Now));

                    chatTextBox.InvokeIfRequired(textBox =>
                    {
                        textBox.AppendText(
                            $"{callback["FirstName"].FirstOrDefault()} {callback["LastName"].FirstOrDefault()}: {text}" +
                            Environment.NewLine);
                    });
                    break;
            }
        }

        private async Task LoadUnseenMessages(MessageDatabase messageDatabase)
        {
            foreach (var message in await messageDatabase.LoadConversations())
            {
                chatListBox.InvokeIfRequired(listBox =>
                {
                    if (message.Seen)
                    {
                        return;
                    }

                    if (listBox.Items.OfType<Conversation>().Any(conversation => conversation.Name == message.Name))
                    {
                        return;
                    }

                    listBox.Items.Add(message);
                });
            }

            foreach (var message in await messageDatabase.LoadSelfConversations())
            {
                chatListBox.InvokeIfRequired(listBox =>
                {
                    if (message.Seen)
                    {
                        return;
                    }

                    if (listBox.Items.OfType<Conversation>().Any(conversation => conversation.Name == message.Name))
                    {
                        return;
                    }

                    listBox.Items.Add(message);
                });
            }

            foreach (var message in await messageDatabase.LoadGroupConversations())
            {
                chatListBox.InvokeIfRequired(listBox =>
                {
                    if (message.Seen)
                    {
                        return;
                    }

                    if (listBox.Items.OfType<Conversation>().Any(conversation => conversation.Name == message.Name))
                    {
                        return;
                    }

                    listBox.Items.Add(message);
                });
            }
        }

        private async Task LoadAllMessages(MessageDatabase messageDatabase)
        {
            foreach (var message in await messageDatabase.LoadConversations())
            {
                chatListBox.InvokeIfRequired(listBox =>
                {
                    if (listBox.Items.OfType<Conversation>().Any(conversation => conversation.Name == message.Name))
                    {
                        return;
                    }

                    listBox.Items.Add(message);
                });
            }

            foreach (var message in await messageDatabase.LoadSelfConversations())
            {
                chatListBox.InvokeIfRequired(listBox =>
                {
                    if (listBox.Items.OfType<Conversation>().Any(conversation => conversation.Name == message.Name))
                    {
                        return;
                    }

                    listBox.Items.Add(message);
                });
            }

            foreach (var message in await messageDatabase.LoadGroupConversations())
            {
                chatListBox.InvokeIfRequired(listBox =>
                {
                    if (listBox.Items.OfType<Conversation>().Any(conversation => conversation.Name == message.Name))
                    {
                        return;
                    }

                    listBox.Items.Add(message);
                });
            }
        }

        #endregion


    }
}

Generated by GNU Enscript 1.6.5.90.