private async void OpenFileClicked(object sender, RoutedEventArgs e)
        {
            if (sender is Button btn)
            {
                if (btn.CommandParameter is string token)
                {
                    DatabaseHelper.Files f = new DatabaseHelper.Files();
                    try
                    {
                        f = await SharedStuff.Database.Table <DatabaseHelper.Files>()
                            .Where(file => file.Token == token).FirstAsync();

                        if (string.IsNullOrEmpty(f.Location))
                        {
                            throw new InvalidOperationException();
                        }
                        if (f.Location == " ") // null means the file is not downloaded. ' ' means it does not exists
                        {
                            throw new FileNotFoundException();
                        }
                        if (File.Exists(f.Location)) // check if the downloaded file exists
                        {
                            System.Diagnostics.Process.Start(f.Location);
                        }
                        else
                        {
                            await SharedStuff.Database.ExecuteAsync(
                                "UPDATE Files SET Location = ? WHERE Token = ?"
                                , " ", token);

                            throw new FileNotFoundException();
                        }
                    }
                    catch (FileNotFoundException)
                    {
                        // at first check if server has the file
                        var err = new ErrorDialogSample
                        {
                            ErrorText =
                            {
                                Text =
                                    "This file does not exists on your computer. Ask other user to send this file again."
                            },
                            ErrorTitle = { Text = "Cannot Open File" }
                        };
                        await DialogHost.Show(err, "ChatDialogHost" + Username);
                    }
                    catch (InvalidOperationException) // this token does not exists in database
                    {
                        // download this file
                        await Application.Current.Dispatcher.Invoke(async delegate
                        {
                            ProgressBar bar = ((StackPanel)((DockPanel)((Button)sender).Parent).Parent)
                                              .Children[1] as ProgressBar;
                            try
                            {
                                string downloadFileUrl =
                                    $"https://{Properties.Settings.Default.ServerAddress}/download?token={token}"; // token does not contain special characters so we are good
                                string destinationFilePath = Path.GetTempFileName();

                                using (var client =
                                           new HttpClientDownloadWithProgress(downloadFileUrl, destinationFilePath))
                                {
                                    client.ProgressChanged +=
                                        (totalFileSize, totalBytesDownloaded, progressPercentage) =>
                                    {
                                        bar.Value = progressPercentage ?? 0;
                                    };

                                    await client.StartDownload();
                                }

                                // get the key
                                string key = (await SharedStuff.Database.Table <DatabaseHelper.Users>()
                                              .Where(user => user.Username == Username).FirstAsync()).Key;
                                BouncyCastleHelper.AesGcmDecrypt(new FileInfo(destinationFilePath),
                                                                 new FileInfo(Path.Combine(SharedStuff.DownloadPath, f.Name)), key);
                                await SharedStuff.Database.ExecuteAsync(
                                    "UPDATE Files SET Location = ? WHERE Token = ?"
                                    , Path.Combine(SharedStuff.DownloadPath, f.Name), token);
                            }
                            catch (FileNotFoundException) // This error means that the files does not exists on server. (it's too old)
                            {
                                await SharedStuff.Database.ExecuteAsync(
                                    "UPDATE Files SET Location = ? WHERE Token = ?"
                                    , " ", token); // this will make the File.Exists always return false
                                MessagesList.First(msg => msg.Token == token).DownloadButtonIcon =
                                    PackIconKind.DownloadOff;
                                var err = new ErrorDialogSample
                                {
                                    ErrorText =
                                    {
                                        Text = "This file is too old and could not be downloaded. Ask other user to send this file again."
                                    },
                                    ErrorTitle = { Text = "Cannot Download File" }
                                };
                                await DialogHost.Show(err, "ChatDialogHost" + Username);
                            }
                            catch (Exception ex)
                            {
                                var err = new ErrorDialogSample
                                {
                                    ErrorText =
                                    {
                                        Text = ex.Message
                                    },
                                    ErrorTitle = { Text = "Cannot Download File" }
                                };
                                await DialogHost.Show(err, "ChatDialogHost" + Username);
                            }
                            finally
                            {
                                bar.Value = 101;
                            }
                        });
                    }
                }
            }
        }
        private async void WS_OnMessage(object sender, MessageEventArgs e)
        {
            // at first try to parse the message as Server status.
            try
            {
                var status = JsonConvert.DeserializeObject <JsonTypes.MessageStatus>(e.Data);
                if (status.Id != null) // a real message status
                {
                    if (!status.Ok)
                    {
                        Console.WriteLine("Error on message " + status.Id + ": " + status.Message);
                    }
                    if (status.Message != "sent") // this is a file token update
                    {
                        SharedStuff.PendingMessages[status.Id].Token = status.Message;
                        // now start upload
                        SharedStuff.UploadFile(status.Id);
                    }
                    else
                    {
                        SharedStuff.PendingMessages[status.Id].Sent = status.Ok ? (byte)0 : (byte)2;
                        // Do not remove files from here; They are removed at "finally" block of SharedStuff.UploadFile
                        if (SharedStuff.PendingMessages[status.Id].Type != 1)
                        {
                            SharedStuff.PendingMessages.Remove(status.Id);
                        }
                    }
                    return;
                }

                if (status.Message != null && !status.Ok)
                {
                    // TODO: HANDLE THIS SHIT
                    return;
                }
            }
            catch (Exception)
            {
                // ignored : the type might be something else
            }
            // try ClientUpdateTypeStruct
            await _mu.WaitAsync(); // get the data in order that server sends them

            string nameSafe = "";

            try
            {
                var msg     = JsonConvert.DeserializeObject <JsonTypes.Updates>(e.Data);
                var keyList = await SharedStuff.Database.Table <DatabaseHelper.Users>().Where(k => k.Username == msg.Payload.From)
                              .ToArrayAsync(); // get the key of the user

                string key;
                if (keyList.Length == 0) // Key agreement
                {
                    var data = await SharedStuff.GetUserData(msg.Payload.From);

                    key = Convert.ToBase64String(SharedStuff.Curve.calculateAgreement(
                                                     Convert.FromBase64String(_savedData.PrivateKey),
                                                     Convert.FromBase64String(data.PublicKey)));
                    await SharedStuff.Database.InsertAsync(new DatabaseHelper.Users // save the key in database
                    {
                        Username = data.Username,
                        Name     = data.Name,
                        Key      = key,
                    });

                    nameSafe = data.Name;
                }
                else // get the key from database; There is only one row because Username is unique
                {
                    key = keyList[0].Key;
                }

                string toShowOnMainMenu           = "";
                JsonTypes.FilePayload filePayload = new JsonTypes.FilePayload();
                switch (msg.Type)
                {
                case 0:     // Text message
                    // decrypt the message or assign the file token
                    try
                    {
                        string message = BouncyCastleHelper.AesGcmDecrypt(msg.Payload.Message, key);
                        await SharedStuff.Database.InsertAsync(new DatabaseHelper.Messages
                        {
                            Username = msg.Payload.From,
                            Type     = msg.Type,
                            Date     = msg.Payload.Date,
                            Payload  = message
                        });

                        toShowOnMainMenu = message;
                    }
                    catch (Exception)
                    {
                        // TODO: RE-REQUEST public key
                    }
                    break;

                case 1:     // File message
                    filePayload = JsonConvert.DeserializeObject <JsonTypes.FilePayload>(msg.Payload.Message);
                    await SharedStuff.Database.RunInTransactionAsync(trans =>
                    {
                        trans.Insert(new DatabaseHelper.Messages
                        {
                            Username = msg.Payload.From,
                            Type     = msg.Type,
                            Date     = msg.Payload.Date,
                            Payload  = filePayload.Token
                        });
                        trans.Insert(new DatabaseHelper.Files
                        {
                            Token = filePayload.Token,
                            Name  = filePayload.FileName
                        });
                    });

                    toShowOnMainMenu = filePayload.FileName;
                    break;
                }

                // save the value
                bool inserted = false, open = OpenWindowsList.ContainsKey(msg.Payload.From);
                int  index = -1;
                try
                {
                    index = MessagesList.IndexOf(MessagesList.First(x =>
                                                                    x.Username == msg.Payload.From)); // get index of the UI
                    if (index == -1)
                    {
                        throw new Exception();
                    }
                }
                catch (Exception)
                {
                    string name = nameSafe;
                    if (name == "")
                    {
                        name = (await SharedStuff.GetUserData(msg.Payload.From)).Name;
                    }
                    Application.Current.Dispatcher.Invoke(delegate
                    {
                        MessagesList.Insert(0, new MainMessagesNotify
                        {
                            FullDate             = msg.Payload.Date,
                            Message              = toShowOnMainMenu,
                            IsLastMessageForUser = false,
                            Name        = name,
                            NewMessages = 1,
                            Username    = msg.Payload.From
                        });
                    });
                    inserted = true;
                }
                if (index == -1)
                {
                    return;
                }

                if (!open) // increase unread messages
                {
                    await SharedStuff.Database.ExecuteAsync("UPDATE Users SET UnreadMessages = ? WHERE Username = ?",
                                                            MessagesList[index].NewMessages + 1, msg.Payload.From);
                }
                if (!inserted)
                {
                    Application.Current.Dispatcher.Invoke(delegate
                    {
                        // Show the user the update
                        MessagesList.Move(index, 0);
                        MessagesList[0].FullDate             = msg.Payload.Date;
                        MessagesList[0].Message              = toShowOnMainMenu;
                        MessagesList[0].IsLastMessageForUser = false;
                        if (!open)
                        {
                            MessagesList[0].NewMessages++;
                        }
                    });
                }

                // update the open windows
                if (open)
                {
                    if (msg.Type == 0) // add text messages
                    {
                        OpenWindowsList[msg.Payload.From].AddMessageText(toShowOnMainMenu, msg.Payload.Date);
                    }
                    else if (msg.Type == 1) // add files
                    {
                        OpenWindowsList[msg.Payload.From].AddMessageFile(filePayload.Token, filePayload.FileName, msg.Payload.Date);
                    }
                }
            }
            catch (Exception ex)
            {
                // TODO: HANDLE THIS SHIT
            }
            finally
            {
                _mu.Release();
            }
        }