using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Forms;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Threading;
using ChatController.Annotations;
using ChatController.ChatKlassen;
using ChatController.Extensions;
using ChatController.HauptKlassen;
using ChatController.LoginKlassen;
using ChatController.Utilities;
using ChatController.Utilities.Extensions;
using Application  = System.Windows.Forms.Application;
using Button       = System.Windows.Controls.Button;
using Cursors      = System.Windows.Input.Cursors;
using DataFormats  = System.Windows.DataFormats;
using KeyEventArgs = System.Windows.Input.KeyEventArgs;
using MenuItem     = System.Windows.Controls.MenuItem;
using MessageBox   = System.Windows.MessageBox;
using Orientation  = System.Windows.Controls.Orientation;
using Panel        = System.Windows.Controls.Panel;
using Path         = System.IO.Path;
using ScrollBar    = System.Windows.Controls.Primitives.ScrollBar;
using Timer        = System.Windows.Forms.Timer;

namespace ChatController
{
    public partial class ChatMainControl : INotifyPropertyChanged
    {
        private readonly Dictionary<long, List<NotifyIcon>> _Group2NotifyIcons = new Dictionary<long, List<NotifyIcon>>();

        private readonly Dictionary<int, SyncFileInfoStruct> _GroupId2DateTime = new Dictionary<int, SyncFileInfoStruct>();

        private string _ThreadExceptionMessage;

        public string ThreadExceptionMessage
        {
            get => _ThreadExceptionMessage;

            set
            {
                if(!Equals(_ThreadExceptionMessage, value))
                {
                    _ThreadExceptionMessage = value;
                    OnPropertyChanged(nameof(ThreadExceptionMessage));
                    OnPropertyChanged(nameof(CurrentContactInformationString));
                    OnPropertyChanged(nameof(CurrencContactInfoForeground));
                    OnPropertyChanged(nameof(ThreadExceptionImageSource));
                }
            }
        }

        private readonly ImageSource _ThreadExceptionImageSource;

        public ImageSource ThreadExceptionImageSource => ThreadExceptionMessage != null ? _ThreadExceptionImageSource : CurrentContact?.Image;

        public SolidColorBrush CurrencContactInfoForeground => ThreadExceptionMessage != null ? new SolidColorBrush(Color.FromRgb(185, 65, 0)) : CurrentContact?.AccentColorBrush ?? new SolidColorBrush(Colors.Transparent);

        public string CurrentContactInformationString => ThreadExceptionMessage ?? CurrentContact?.Name;

        private Window _ContainingWindow;

        public Window ContainingWindow
        {
            get => _ContainingWindow;

            set
            {
                _ContainingWindow = value;

                if(value != null)
                {
                    _ContainingWindow.Deactivated += ContainingWindowOnDeactivated;
                    _ContainingWindow.Activated += ContainingWindowOnActivated;
                }
            }
        }

        public Chat Chat { get; set; }
        private Contact _CurrentContact;

        public Contact CurrentContact
        {
            get => _CurrentContact;

            set
            {
                if(!Equals(_CurrentContact, value))
                {
                    _CurrentContact = value;
                    
                    OnPropertyChanged(nameof(CurrentContact));
                    OnPropertyChanged(nameof(ChatMessageInputGridVisibility));
                    OnPropertyChanged(nameof(Chat));
                    OnPropertyChanged(nameof(CurrentContactInformationString));
                    OnPropertyChanged(nameof(CurrencContactInfoForeground));
                    OnPropertyChanged(nameof(ThreadExceptionImageSource));
                }
            }
        }

        public ObservableCollection<Contact> ContactList
        {
            get
            {
                return _ContactList == null ? new ObservableCollection<Contact>() : new ObservableCollection<Contact>(_ContactList.OrderByDescending(contact => contact.TimeStamp));
            }
        }

        public Visibility ChatMessageInputGridVisibility
        {
            get
            {
                if(_CurrentContact is null)
                {
                    return Visibility.Collapsed;
                }

                return _CurrentContact.IsChatMessageInputGridVisible ? Visibility.Visible : Visibility.Collapsed;
            }
        }

        private List<Contact> _ContactList;
        
        private bool _ScrollPrueferAktivieren;

        public bool ShouldInterruptContactsThread = true;

        private readonly List<string> _CreatedTempFiles = new List<string>();

        private readonly Dictionary<string, Action<string>> _MenuItemName2Callback = new Dictionary<string, Action<string>>();

        private bool _IsDesktopVersion = true;

        private bool _IsInBackground;

        public ChatMainControl()
        {
            InitializeComponent();
            
            _ThreadExceptionImageSource = new BitmapImage(new Uri("pack://application:,,,/ChatController;component/Ressourcen/warning-exclamation-mark.png", UriKind.Absolute));

            _ChatMessages = new ObservableCollection<ChatMessage>();
            ChatMessages = CollectionViewSource.GetDefaultView(_ChatMessages) as ListCollectionView;
            if (!(ChatMessages is null))
            {
                ChatMessages.CustomSort = new ChatMessageComparer();
            }

            DataContext = this;

            ReloadGruppen.Visibility = Visibility.Collapsed;
        }

        private void ContainingWindowOnDeactivated(object sender, EventArgs e)
        {
            _IsInBackground = true;
        }

        private void ContainingWindowOnActivated(object sender, EventArgs e)
        {
            _IsInBackground = false;
        }

        private readonly ObservableCollection<ChatMessage> _ChatMessages;
        public ListCollectionView ChatMessages { get; set; }

        public void InitMitChatdaten(ChatDatenUebergabe cdu)
        {
            Chat = new Chat(cdu, exception => {
                    this.Dispatch(
                        () => {
                            EndWaiting();
                            
                            MessageBox.Show($"Fehler: {exception.Message}", "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
                        });
                },
                exception => {
                    this.Dispatch(
                        () => {
                            if(exception is null)
                            {
                                ThreadExceptionMessage = null;
                            }

                            ThreadExceptionMessage = exception?.Message?.Contains("500") ?? false ? "ownChat ist vorrübergehend nicht verfügbar. Bitte versuchen Sie es später noch einmal." : null;
                        });
                });

            OnPropertyChanged(nameof(Chat));

            _ContactList = Chat.AddContacts();

            OnPropertyChanged(nameof(ContactList));

            ReadNewSyncFile();

            foreach (var contact in _ContactList)
            {
                if (_GroupId2DateTime.ContainsKey(contact.GroupId))
                {
                    var isUnread = _GroupId2DateTime[contact.GroupId].IsUnread;

                    contact.IsNewMessage = isUnread;
                    contact.HasUnreadMessages = contact.IsNewMessage;
                }
            }

            GlobalListeningThreadtimer?.Stop();
            GlobalListeningThreadtimer = null;
            InitGlobalListeningThread();

            Clientlist.Items.Refresh();
        }

        public void ShowNotification(string pTitle, GroupLatestMessage pMessage, int pGroupId)
        {
            var notificationIcon = Resource.ownchat_favicon;

            var contact = _ContactList.FirstOrDefault(a => a.GroupId == pGroupId);

            if (contact != null)
            {
                notificationIcon = Utils.ImageSourceToIcon(contact.Image);
            }

            var notifyIcon = new NotifyIcon { Icon = notificationIcon, Visible = true, Tag = pMessage };

            notifyIcon.BalloonTipClicked += (sender, args) =>
            {
                DisposeAndRemoveNotification((NotifyIcon)sender, pGroupId);

                if(_ContainingWindow == null)
                {
                    return;
                }

                if (_ContainingWindow.WindowState == WindowState.Minimized)
                {
                    _ContainingWindow.WindowState = WindowState.Normal;
                }

                _ContainingWindow.Activate();

                var selectedContact = _ContactList?.FirstOrDefault(f => f?.GroupId == pGroupId);
                if(selectedContact != null)
                {
                    SelectContact(selectedContact);
                }
            };
            
            notifyIcon.BalloonTipClosed += (sender, args) =>
            {
                // TODO: wird beim ausblenden bzw. automatischem Schließen des "Balloons" ausgelöst
                DisposeAndRemoveNotification((NotifyIcon)sender, pGroupId);
            };

            _Group2NotifyIcons.AddOrUpdateValueInDictionary(pGroupId, notifyIcon);

            _Group2NotifyIcons[pGroupId].Find(f => f.Tag is GroupLatestMessage message && message.Id.Equals(pMessage.Id))?.ShowBalloonTip(Constants.NotificationTimeout, pTitle, "Sie haben eine neue Nachricht", ToolTipIcon.None);
        }

        public void ClearNotifications()
        {
            foreach(var group2NotifyIcon in _Group2NotifyIcons)
            {
                foreach(var notifyIcon in group2NotifyIcon.Value)
                {
                    notifyIcon.Icon = null;
                    notifyIcon.Dispose();
                    Application.DoEvents();
                }
            }

            _Group2NotifyIcons.Clear();
        }

        private void DisposeAndRemoveNotification(IDisposable pNotification, long pGroupId)
        {
            pNotification.Dispose();
            _Group2NotifyIcons.Remove(pGroupId);
            
            Application.DoEvents();
        }

        public void ResetStyle()
        {
            EmojiButton.Style = null;
            MediaButton.Style = null;
            ReloadGruppen.Visibility = Visibility.Visible;
            _IsDesktopVersion = false;
        }

        private void CurrentContactImage_OnMouseDoubleClick(object sender, MouseButtonEventArgs e)
        {
            if(e.ClickCount == 2)
            {
                Chat.ShowProfilePicture(_CurrentContact);
            }
        }

        private void Clientlist_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if(Clientlist?.SelectedItem is Contact selectedContact)
            {
                SelectContact(selectedContact);
            }
        }

        private void SelectContact(Contact pContact)
        {
            try
            {
                if (pContact == null)
                {
                    return;
                }

                ShouldInterruptContactsThread = true;

                StartWaitingImmediately();

                _ScrollPrueferAktivieren = false;

                if (pContact.HasUnreadMessages)
                {
                    pContact.HasUnreadMessages = false;

                    Clientlist.Items.Refresh();
                    OnPropertyChanged(nameof(ContactList));
                }

                var currentContact = pContact;
                pContact.IsNewMessage = false;

                var shouldChangeIcon = _ContactList.Any(contact => contact.IsNewMessage);
                if (shouldChangeIcon)
                {
                    ContainingWindow.Icon = Utils.ConvertIconToImageSource(Resource.ownchat_favicon);
                }

                CurrentContact = currentContact;

                if (Clientlist.Items.Contains(pContact))
                {
                    Clientlist.SelectedItem = pContact;
                }

                _ChatMessages.Clear();
                OnPropertyChanged(nameof(_ChatMessages));

                Chat.LoadChatMessagesForContactAsync(currentContact, GetFirstMessage(), chatMessages =>
                {
                    this.Dispatch(() =>
                    {
                        _ChatMessages.Clear();
                        _ChatMessages.AddRangeIfElementsNotIn(chatMessages);
                        OnPropertyChanged(nameof(_ChatMessages));

                        WpfUtils.ScrollToBottomOfListBox(ChatListBox);

                        ChatListBox.ContextMenu = Chat.ErstelleKontextMenue();
                        SetContextHandler();
                        _ScrollPrueferAktivieren = true;

                        ListenForMessages();
                        Cursor = Cursors.Arrow;

                        WriteToNewSyncFile();

                        ShouldInterruptContactsThread = false;

                        EndWaiting();
                    });
                });
            }
            catch(Exception exception)
            {
#if DEBUG
                var desktop = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory);

                using(var fileStream = new FileStream(Path.Combine(desktop, "ownchat_log.txt"), FileMode.OpenOrCreate))
                {
                    using(var streamWriter = new StreamWriter(fileStream))
                    {
                        streamWriter.WriteLine(exception.ToString());
                    }
                }
#endif

                MessageBox.Show(exception.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }

        private void SetContextHandler()
        {
            var contextItems = ChatListBox?.ContextMenu?.Items;

            if(contextItems == null)
            {
                return;
            }

            foreach (var menuItemText in _MenuItemName2Callback.Keys)
            {
                var callback = _MenuItemName2Callback[menuItemText];

                var menuItem = new MenuItem();

                menuItem.Click += (s, e2) =>
                {
                    callback(menuItemText);
                };

                menuItem.Header = menuItemText;

                contextItems.Add(menuItem);
            }

            if(contextItems.Count > 0)
            {
                var saveAsMenuItem = (MenuItem) contextItems[0];
                saveAsMenuItem.Click += SaveAsOnClick;
            }

            if(contextItems.Count > 1)
            {
                var pasteMenuItem = (MenuItem) contextItems[1];
                pasteMenuItem.Click += PasteOnClick;
            }

            // neu und vor Einfügen setzen und anpassen
            if(contextItems.Count > 2)
            {
                var copyMenuItem = (MenuItem) contextItems[2];
                copyMenuItem.Click += CopyOnClick;
            }
        }

        private void SaveAsOnClick(object sender, RoutedEventArgs routedEventArgs)
        {
            foreach (var items in ChatListBox.SelectedItems)
            {
                var chatMessage = (ChatMessage) items;

                Chat.SaveFileAs(chatMessage);
            }
        }

        private void PasteOnClick(object sender, RoutedEventArgs routedEventArgs)
        {
            Chat.Paste(CurrentContact.GroupId,this);          
        }

        private void CopyOnClick(object sender, RoutedEventArgs routedEventArgs)
        { 
            var chatMessages = string.Empty;

            foreach (var items in ChatListBox.SelectedItems)
            {
                var item = (ChatMessage) items;

                chatMessages += item.UserMessage + "\n";
            }

            if(!string.IsNullOrWhiteSpace(chatMessages))
            {
                Chat.Copy(chatMessages, 1);
            }
        }

        private void Chat_OnContextMenuOpening(object sender, ContextMenuEventArgs e)
        {
            if(ChatListBox?.ContextMenu == null)
            {
                return;
            }

            if(ChatListBox.SelectedItems is List<ChatMessage> chatMessages)
            {
                if(chatMessages.Count > 0)
                {
                    foreach(var items in chatMessages)
                    {
                        var item = (ChatMessage) items;
                        if(item?.PictureSource != null)
                        {
                            if(ChatListBox.ContextMenu != null)
                            {
                                var contextItems = ChatListBox.ContextMenu.Items;
                                ((MenuItem)contextItems[0]).Visibility = Visibility.Visible;
                            }
                        }
                        else
                        {
                            var contextItems = ChatListBox.ContextMenu.Items;
                            var contextItemSpeichernUnter = (MenuItem)contextItems[0];
                            contextItemSpeichernUnter.Visibility = Visibility.Collapsed;
                        }

                        if(item.PictureSource == null && item.FilePath == null)
                        {
                            var contextItems = ChatListBox.ContextMenu.Items;
                            var contextItemSpeichernUnter = (MenuItem)contextItems[2];
                            contextItemSpeichernUnter.Visibility = Visibility.Visible;
                        }
                        else
                        {
                            var contextItems = ChatListBox.ContextMenu.Items;
                            var contextItemSpeichernUnter = (MenuItem)contextItems[2];
                            contextItemSpeichernUnter.Visibility = Visibility.Collapsed;
                        }
                    }
                }
                else
                {
                    //Schalte speichern unter Aus 
                    var contextItems = ChatListBox.ContextMenu.Items;
                    var contextItemSpeichernUnter = (MenuItem)contextItems[0];
                    contextItemSpeichernUnter.Visibility = Visibility.Collapsed;
                }

                //prüfe ob was in dem Speicher vorhanden ist //Einfügen
                var dataObject = System.Windows.Forms.Clipboard.GetDataObject();

                if(dataObject != null && dataObject.GetDataPresent(DataFormats.FileDrop))
                {
                    var contextItems = ChatListBox.ContextMenu.Items;
                    var contextItemSpeichernUnter = (MenuItem) contextItems[1];
                    contextItemSpeichernUnter.Visibility = Visibility.Visible;
                }
                else if(dataObject != null && dataObject.GetDataPresent(DataFormats.Text))
                {
                    var contextItems = ChatListBox.ContextMenu.Items;
                    var contextItemSpeichernUnter = (MenuItem) contextItems[1];
                    contextItemSpeichernUnter.Visibility = Visibility.Visible;
                }
                else if(dataObject != null && dataObject.GetDataPresent(DataFormats.Bitmap))
                {
                    var contextItems = ChatListBox.ContextMenu.Items;
                    var contextItemSpeichernUnter = (MenuItem) contextItems[1];
                    contextItemSpeichernUnter.Visibility = Visibility.Visible;
                }
                else
                {
                    var contextItems = ChatListBox.ContextMenu.Items;
                    var contextItemSpeichernUnter = (MenuItem)contextItems[1];
                    contextItemSpeichernUnter.Visibility = Visibility.Collapsed;
                }

                // Prüfe, ob Dokumentation erlaubt
                if(CurrentContact.UserIdManage == null && ChatListBox.ContextMenu.Items.Count > 3)
                {
                    var contextItems = ChatListBox.ContextMenu.Items;
                    var contextItemSpeichernUnter = (MenuItem)contextItems[3];
                    contextItemSpeichernUnter.Visibility = Visibility.Collapsed;
                }
                else if(ChatListBox.ContextMenu.Items.Count > 3)
                {
                    var contextItems = ChatListBox.ContextMenu.Items;
                    var contextItemSpeichernUnter = (MenuItem)contextItems[3];
                    contextItemSpeichernUnter.Visibility = Visibility.Visible;
                }

                if(ChatListBox.ContextMenu.Items.Count == 0)
                {
                    ChatListBox.ContextMenu.Visibility = Visibility.Collapsed;
                }
            }
        }

        private void MediaButton_OnClick(object sender, RoutedEventArgs e)
        {
            if(CurrentContact != null)
            {
                var pathToOriginalImage = Chat.OpenFile();

                if(pathToOriginalImage != null) 
                {
                    var pathToScaledImage = FileUtils.ScaleImage(pathToOriginalImage, Path.GetExtension(pathToOriginalImage.ToUpperInvariant()), Chat.ChatDaten.MaxUploadSize);

                    var isFileSizeTooLarge = FileUtils.CheckFileSize(pathToScaledImage, Chat.ChatDaten.MaxUploadSize);

                    if(isFileSizeTooLarge)
                    {
                        MessageBox.Show($"Die ausgewählte Datei ist zu groß. Die maximale Größe beträgt {Chat.ChatDaten.MaxUploadSize / 1000 / 1000} MB", "Senden nicht möglich", MessageBoxButton.OK, MessageBoxImage.Warning);
                        return;
                    }

                    if (!string.IsNullOrWhiteSpace(pathToScaledImage))
                    {
                        _ChatMessages.AddRangeIfElementsNotIn(Chat.AddNewFile(pathToScaledImage, pathToOriginalImage, CurrentContact.GroupId, GetFirstMessage()));

                        ChatMessages.MoveCurrentToLast();

                        var currentChatMessage = ChatMessages.CurrentItem;

                        ChatListBox.ScrollIntoView(currentChatMessage);

                        Chat.SendFileToContact(CurrentContact, pathToScaledImage,pathToOriginalImage);

                        if (!string.IsNullOrWhiteSpace(pathToScaledImage) && !pathToScaledImage.Equals(pathToOriginalImage) && File.Exists(pathToScaledImage))
                        {
                            File.Delete(pathToScaledImage);
                        }
                    }
                }
            }
            else
            {
                MessageBox.Show("Bitte wählen Sie einen Kontakt aus.","ownChat",MessageBoxButton.OK, MessageBoxImage.Information);
            }
        }

        private void SendButton_OnClick(object sender, RoutedEventArgs e)
        {
            if(CurrentContact != null && !string.IsNullOrEmpty(Chatbox.Text))
            {
                var messages2Add = Chat.AddNewMessage(Chatbox.Text, CurrentContact.GroupId, GetFirstMessage());

                _ChatMessages.AddRangeIfElementsNotIn(messages2Add);
                OnPropertyChanged(nameof(ChatMessages));
                ChatMessages.MoveCurrentToLast();

                ChatListBox.ScrollIntoView(ChatMessages.CurrentItem);

                WpfUtils.ScrollToBottomOfListBox(ChatListBox);

                Chat.SendMessage(Chatbox.Text, CurrentContact.GroupId);

                Chatbox.Text = string.Empty;

                Thread.Sleep(500);

                DoSynchro();
            }
        }

        private void Chatbox_OnGotFocus(object sender, RoutedEventArgs e)
        {
            if (Chatbox.Text.Equals("Nachricht schreiben"))
            {
                Chatbox.Text = string.Empty;
                Chatbox.Foreground = new SolidColorBrush(Colors.Black);
            }
        }

        private void Chatbox_OnKeyDownHandler(object sender, KeyEventArgs e)
        {
            try
            {
                //if (e.Key == Key.Return)
                //{
                //    if(CurrentContact != null && !string.IsNullOrEmpty(Chatbox.Text))
                //    {
                //        Chat.AddNewMessage(Chatbox.Text, CurrentContact.GroupId);

                //        OnPropertyChanged(nameof(CurrentChatMessages));

                //        ChatListBox.Items.MoveCurrentToLast();
                //        ChatListBox.ScrollIntoView(ChatListBox.Items.CurrentItem);
                       
                //        Chat.SendMessage(Chatbox.Text, CurrentContact.GroupId);

                //        Chatbox.Text = string.Empty;
                //    }
                //}
            }
            catch (Exception ed)
            {
                MessageBox.Show("Beim Senden einer Nachricht ist ein Fehler aufgetreten. " + ed.Message + "\n" + ed.StackTrace, "Fehler", MessageBoxButton.OK);
            }
        }

        public delegate void EmojiiDelegate(Button button);

        public event EmojiiDelegate OnEmojii;

        private void EmojiButton_OnClick(object sender, RoutedEventArgs e)
        {
            OnEmojii?.Invoke(EmojiButton);
        }
        
        private void Chat_OnScrollChanged(object sender, ScrollChangedEventArgs e)
        {
            var scrollBarList = Utils.GetVisualChildCollection<ScrollBar>(ChatListBox);
            foreach (var scrollBar in scrollBarList)
            {
                if (scrollBar.Orientation == Orientation.Horizontal)
                {
                    _ScrollPrueferAktivieren = true;
                }
                else
                {
                    scrollBar.ValueChanged += VerticalScrollbarChanged;
                }
            }
        }

        private void VerticalScrollbarChanged(object sender, RoutedPropertyChangedEventArgs<double> routedPropertyChangedEventArgs)
        {
            if (_ScrollPrueferAktivieren)
            {
                var scrollBar = (ScrollBar) sender;

                if (!(scrollBar.Value > 0))
                {
                    StartWaitingImmediately();

                    _ScrollPrueferAktivieren = false;

                    ChatMessages.MoveCurrentToFirst();

                    var currentChatMessage = ChatMessages.CurrentItem;
                    
                    var selectedContact = (Contact) Clientlist.SelectedItem;

                    Chat.LoadMoreMessagesAsync(selectedContact, GetFirstMessage(), chatMessages =>
                    {
                        this.Dispatch(() =>
                        {
                            EndWaiting();

                            _ChatMessages.AddRangeIfElementsNotIn(chatMessages);

                            ChatListBox.ScrollIntoView(currentChatMessage);
                        });
                    });
                }
            }         
        }

        private void ListenForMessages()
        {
            EmojisListeningThreadTimer?.Stop();
            EmojisListeningThreadTimer = null;
            InitListeningThread(CurrentContact);
        }

        public Timer EmojisListeningThreadTimer;
        public Timer GlobalListeningThreadtimer { get; set; }

        private void InitGlobalListeningThread()
        {
            GlobalListeningThreadtimer = new Timer();
            GlobalListeningThreadtimer.Tick += GlobalListeningThreadTimerTickEvent;
            GlobalListeningThreadtimer.Interval = 10000;
            GlobalListeningThreadtimer.Start();
        }

        private void GlobalListeningThreadTimerTickEvent(object sender, EventArgs e)
        {
            ListenGloballyForMessages();
        }

        private void ListenGloballyForMessages()
        {
            Chat.GetNumberOfAllNewMessagesAsync(newMessagesCount =>
            {
                if(newMessagesCount > 0)
                {
                    Chat.UpdateContactList(updatedContactList =>
                    {
                        var updatedContacts = updatedContactList.ToList();

                        this.Dispatch(() =>
                        {
                            foreach (var contact in _ContactList)
                            {
                                foreach (var newContact in updatedContacts)
                                {
                                    if (contact.GroupId == newContact.GroupId)
                                    {
                                        // ReceivedMessage kann null sein!
                                        if (contact.ReceivedMessage != null && !contact.ReceivedMessage.Id.Equals(newContact.ReceivedMessage?.Id))
                                        {
                                            contact.ReceivedMessage = newContact.ReceivedMessage;
                                            contact.TimeStamp = newContact.TimeStamp;

                                            var selectedContact = (Contact)Clientlist.SelectedItem;
                                            // Angemeldeter Benutzer
                                            var userOid = Chat.ChatDaten.LoggedInUser.Response.User.Oid;

                                            var senderId = contact.ReceivedMessage?.UserId;
                                            var receiverId = userOid;
                                            var receiverGroupId = newContact.GroupId;
                                            var selectedGroupId = selectedContact?.GroupId;

                                            if (senderId != receiverId && (_IsInBackground || receiverGroupId != selectedGroupId))
                                            {
                                                if (ContainingWindow != null)
                                                {
                                                    ContainingWindow.Icon = Utils.ConvertIconToImageSource(Resource.oC_favico_NewMessage);
                                                }

                                                ShowNotification(newContact.Name, newContact.ReceivedMessage, newContact.GroupId);
                                            }

                                            // Wenn der momentan ausgewählte Kontakt der aktuelle Kontakt in der Schleife ist, werden die Nachrichten nicht als ungelesen angezeigt, sonst schon.
                                            if (CurrentContact != null && newContact.GroupId == CurrentContact.GroupId)
                                            {
                                                contact.HasUnreadMessages = false;
                                            }
                                            else
                                            {
                                                contact.HasUnreadMessages = true;
                                                contact.IsNewMessage = true;
                                            }
                                        }
                                    }
                                }

                                if (!updatedContacts.Contains(contact))
                                {
                                    _ContactList.ToList().Remove(contact);
                                }
                            }

                            Clientlist.Items.Refresh();
                            OnPropertyChanged(nameof(ChatMessages));
                            OnPropertyChanged(nameof(ContactList));
                        });
                    });
                }
            });
        }

        private void InitListeningThread(Contact currentContact)
        {
            EmojisListeningThreadTimer = new Timer {Tag = currentContact};
            EmojisListeningThreadTimer.Tick += ListeningThreadTimerTickEvent;
            EmojisListeningThreadTimer.Interval = 10000;
            EmojisListeningThreadTimer.Start();
        }

        private void ListeningThreadTimerTickEvent(object sender, EventArgs e)
        {
            ListenForMessagesForCurrentContact((Contact) ((Timer) sender).Tag);
        }

        private void ListenForMessagesForCurrentContact(Contact pCurrentContact)
        {
            if (!ShouldInterruptContactsThread)
            {
                Chat.CheckIfNewMessagesExistAsync(CurrentContact.GroupId, hasNewMessages =>
                {
                    if(!hasNewMessages)
                    {
                        return;
                    }

                    Chat.LoadChatMessagesForContactAsync(pCurrentContact, GetFirstMessage(), chatMessages =>
                    {
                        this.Dispatch(() =>
                        {
                            _ChatMessages.Clear();
                            _ChatMessages.AddRangeIfElementsNotIn(chatMessages);

                            WpfUtils.ScrollToBottomOfListBox(ChatListBox);
                        });
                    });
                });
            }
        }

        private void Suche_OnTextChanged(object sender, TextChangedEventArgs e)
        {
            CollectionViewSource.GetDefaultView(Clientlist.ItemsSource).Refresh();
        }

        private bool UserFilter(object item)
        {
            if (string.IsNullOrEmpty(Suche.Text))
            {
                return true;
            }

            return (item as Contact).Name.IndexOf(Suche.Text, StringComparison.OrdinalIgnoreCase) >= 0;
        }

        private void Chat_OnMouseDoubleClick(object sender, MouseButtonEventArgs e)
        {
            if(ChatListBox.SelectedItems.Count > 0)
            {
                var selectedItem = ChatListBox.SelectedItems[0];

                if(selectedItem is ChatMessage selectedMessage)
                {
                    if (!string.IsNullOrEmpty(selectedMessage.OriginalImage))
                    {
                        var uri = new Uri(selectedMessage.OriginalImage);

                        if(uri.IsFile)
                        {
                            var bitmapImage = new BitmapImage(uri);
                            bitmapImage.Freeze();
                            Chat.ShowPictureWindow(bitmapImage, "Test");

                            return;
                        }

                        StartWaitingImmediately();

                        Chat.ShowPicture(selectedMessage.OriginalImage, CurrentContact.GroupId, (imageSource, windowTitle) =>
                        {
                            this.Dispatch(() =>
                            {
                                EndWaiting();
                                Chat.ShowPictureWindow(imageSource, windowTitle);
                            });
                        });
                    }
                    else if(selectedMessage.FilePath != null)
                    {
                        StartWaitingImmediately();

                        LoadDocumentAsync(selectedMessage.FilePath.ToString(), Path.GetFileName(selectedMessage.FilePath.ToString()), EndWaiting);
                    }
                }
            }
        }

        private void LoadDocumentAsync(string uri, string fileName, Action callback)
        {
            try
            {
                var path = Path.Combine(Path.GetTempPath(), fileName);

                _CreatedTempFiles.Add(path);

                if(!File.Exists(path))
                {
                    Chat.DownloadFileAsync(uri, path, () =>
                    {
                        this.Dispatch(() =>
                        {
                            if (File.Exists(path))
                            {
                                Process.Start(path);
                            }

                            callback?.Invoke();
                        });
                    });
                }
                else
                {
                    Process.Start(path);

                    callback?.Invoke();
                }
            }
            catch(Exception exception)
            {
                EndWaiting();

                MessageBox.Show(exception.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }

        private ChatControlWaitLayer _WaitLayer;

        public void StartWaiting()
        {
            Dispatcher.BeginInvoke(DispatcherPriority.Background, (Action)StartWaitingImmediately);
        }

        public void StartWaitingImmediately()
        {
            if (_WaitLayer == null)
            {
                _WaitLayer = new ChatControlWaitLayer();
                Grid.SetRowSpan(_WaitLayer, 3);
                RootGrid.Children.Add(_WaitLayer);
                Panel.SetZIndex(_WaitLayer, int.MaxValue);
                _WaitLayer.RefreshChatUI();               
            }
        }

        public void EndWaiting()
        {
            Dispatcher.BeginInvoke(
                DispatcherPriority.Background,
                (Action) delegate
                {
                    if(_WaitLayer != null)
                    {
                        RootGrid.Children.Remove(_WaitLayer);
                        _WaitLayer = null;
                    }
                });
        }

        private void ButtonReloadGruppen_OnClick(object sender, RoutedEventArgs e)
        {        
            DoSynchro();
        }
        
        private void DoSynchro()
        {
            try
            {
                StartWaitingImmediately();

                WriteToNewSyncFile();

                Chat.ReloadGroupsAsync(data =>
                {
                    this.Dispatch(() =>
                    {
                        _ContactList = Chat.UpdateContacts(data); // <- dauert eine Sekunde!
                        OnPropertyChanged(nameof(ContactList));

                        ReadNewSyncFile();

                        AddViewFilterToClientlist();

                        EndWaiting();
                    });
                });
            }
            catch (Exception e)
            {
                EndWaiting();
                MessageBox.Show("Ein Fehler bei der Synchronisation ist aufgetreten.\nFehler:\n" + e.Message, "Fehler", MessageBoxButton.OK);
            }
        }
        
        // Speichert die TimeStamps der zuletzt empfangenen Nachrichten
        public void WriteToNewSyncFile()
        {
            try
            {
                if (File.Exists(Path.Combine(Utils.GetAndCreateUserAppDataPath(), Resource.SyncFile2Name)))
                {
                    File.SetAttributes(Path.Combine(Utils.GetAndCreateUserAppDataPath(), Resource.SyncFile2Name), FileAttributes.Normal);
                    File.Delete(Path.Combine(Utils.GetAndCreateUserAppDataPath(), Resource.SyncFile2Name));
                }

                using (var streamWriter = new StreamWriter(Path.Combine(Utils.GetAndCreateUserAppDataPath(), Resource.SyncFile2Name)))
                {
                    Encoding enc = new UTF8Encoding();

                    foreach (var contact in _ContactList.ToList().Where(contact => contact.ReceivedMessage != null))
                    {
                        var text = contact.GroupId + ";" + contact.TimeStamp.ToUniversalTime() + ";" + contact.IsNewMessage + ";";
                        var bytes = enc.GetBytes(text);
                        var base64String = Convert.ToBase64String(bytes);

                        streamWriter.WriteLine(base64String);
                    }

                    File.SetAttributes(Path.Combine(Utils.GetAndCreateUserAppDataPath(), Resource.SyncFile2Name), FileAttributes.ReadOnly);
                }
            }
            catch (Exception exception)
            {

            }
        }

        public void ReadNewSyncFile()
        {
            try
            {
                if(File.Exists(Path.Combine(Utils.GetAndCreateUserAppDataPath(), Resource.SyncFile2Name)))
                {
                    using (var streamReader = new StreamReader(Path.Combine(Utils.GetAndCreateUserAppDataPath(), Resource.SyncFile2Name), true))
                    {
                        string line;

                        _GroupId2DateTime.Clear();

                        while ((line = streamReader.ReadLine()) != null)
                        {
                            var encodedTextBytes = Convert.FromBase64String(line);
                            var plainText = Encoding.UTF8.GetString(encodedTextBytes);
                            var splitPlainText = plainText.Split(';');

                            // Ist kein Datum vorhanden -> 01.01.0001 00:00:00

                            var groupId = int.Parse(splitPlainText[0]);
                            var dateTime = DateTime.Parse(splitPlainText[1]);
                            var isUnread = splitPlainText.Length <= 2 || string.IsNullOrEmpty(splitPlainText[2]) || bool.Parse(splitPlainText[2]);

                            _GroupId2DateTime.Add(groupId, new SyncFileInfoStruct(isUnread, dateTime));
                        }
                    }

                    var latestDateTime = Utils.DefaultDate;

                    foreach(var id2dateTime in _GroupId2DateTime)
                    {
                        if(id2dateTime.Value.TimeStamp > latestDateTime)
                        {
                            latestDateTime = id2dateTime.Value.TimeStamp;
                        }
                    }

                    Chat.LastTimeStamp = latestDateTime.GetUnixTimeStamp();
                }
            }
            catch (Exception e)
            {
                
            }
        }

        // Wird im BeWoPlaner benutzt
        public List<Contact> GetContactsWithoutUnreadMessages()
        {
            var contacts = new List<Contact>();

            Clientlist.Items.Refresh();

            foreach(var contactItem in Clientlist.Items)
            {
                if(contactItem is Contact contact && contact.HasUnreadMessages && !string.IsNullOrEmpty(contact.ReceivedMessage?.Text))
                {
                    contacts.Add(contact);
                }
            }

            return contacts;
        }

        // Wird im BeWoPlaner benutzt
        public void ResetNumberOfUnreadMessages(Dictionary<long, DateTime> pGroupId2DateTime)
        {
            if(pGroupId2DateTime != null)
            {
                foreach(var contact in _ContactList)
                {
                    foreach(var groupId2DateTime in pGroupId2DateTime)
                    {
                        if(contact.GroupId == groupId2DateTime.Key)
                        {
                            contact.HasUnreadMessages = false;
                        }
                    }
                }
            }
        }

        // Wird im BeWoPlaner benutzt
        public List<ChatMessage> GetSelectedMessages()
        {
            var messages = (from object selectedItem in ChatListBox.SelectedItems select selectedItem as ChatMessage).ToList();

            return messages.Count > 0 ? messages : null;
        }

        // Wird im BeWoPlaner benutzt
        public Contact GetCurrentContactWithUserIdManage()
        {
            return CurrentContact?.UserIdManage != null ? CurrentContact : null;
        }

        // Wird im BeWoPlaner benutzt
        public Contact GetCurrentContact()
        {
            return CurrentContact;
        }

        // Wird im BeWoPlaner benutzt
        public void AddContextMenu(string menuItemText, Action<string> callBackAction)
        {
            if (!_MenuItemName2Callback.ContainsKey(menuItemText))
            {
                _MenuItemName2Callback.Add(menuItemText, callBackAction);
            }
        }

        public void DeleteTempFiles()  
        {
            foreach (var file in _CreatedTempFiles)
            {
                try
                {
                    if (File.Exists(file))
                    {
                        File.Delete(file);
                    }
                }
                catch (Exception e)
                {
                    //ignore
                }
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        [NotifyPropertyChangedInvocator]
        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }

        private void ChatMainControl_OnLoaded(object sender, RoutedEventArgs e)
        {
            AddViewFilterToClientlist();
        }

        private void AddViewFilterToClientlist()
        {
            var view = (CollectionView)CollectionViewSource.GetDefaultView(Clientlist.ItemsSource);
            view.Filter = UserFilter;
        }

        private void Chatbox_OnTextChanged(object sender, TextChangedEventArgs e)
        {
            if(string.IsNullOrWhiteSpace(Chatbox.Text))
            {
                Chatbox.Height = 25;
            }
        }

        public void AddMessages(List<ChatMessage> newMessages)
        {
            _ChatMessages.AddRangeIfElementsNotIn(newMessages);
        }

        public ChatMessage GetFirstMessage()
        {
            return _ChatMessages.FirstOrDefault();
        }
    }
}
