using Avalonia.Controls.ApplicationLifetimes;
using ReactiveUI;
using System;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using VeloeMinecraftLauncher.Entity.LauncherProfiles;
using VeloeMinecraftLauncher.MinecraftLauncher;
using VeloeMinecraftLauncher.Views;
using Microsoft.AspNetCore.SignalR.Client;
using VeloeMinecraftLauncher.Entity.McStatus;
using System.Timers;
using System.Reflection;
using Serilog;
using Avalonia.Controls;
using Avalonia.Threading;
using System.Net;
using System.IO.Compression;
using ReactiveUI.Validation.Extensions;
using Avalonia.Media;
using Avalonia.Data;
using System.Collections.Generic;
using VeloeMinecraftLauncher.Models.Entity;
using VeloeMinecraftLauncher.Models;
using System.Collections.Specialized;
using System.Linq;

namespace VeloeMinecraftLauncher.ViewModels;

public class MainWindowViewModel : ViewModelBase
{
    public MainWindowViewModel()
    {           
        Task.Run(() =>
        {
            try
            {
                //creating logger
                EventSink eventSink = new(null);
                eventSink.DataReceived += LogHandler;

                _logger = new LoggerConfiguration()
                    .MinimumLevel.Debug()
                    .WriteTo.Sink(eventSink , Settings.consoleLogEventLevel)
                    .WriteTo.File("launcher.log", Settings.fileLogEventLevel, fileSizeLimitBytes: Settings.maxLog * 1024, rollOnFileSizeLimit: true)// restricted... is Optional
                    .CreateLogger();
                Settings.logger = _logger;

                //loading settings
                _logger.Debug("Loading settings.");
                Settings.LoadSettings();
                _username = Settings.username;

                //loading local verions
                _logger.Debug("Loading local versions.");
                updateAvailable();
            }
            catch (Exception ex)
            {
                OpenErrorWindow(ex);
            }

            this.ValidationRule(
                viewModel => viewModel.Username,
                value => { return !string.IsNullOrEmpty(value); },
                "Empty username.");

            UpdateUpdater();

            try
            {
                //check launcher update
                _logger.Information("Checking launcher versions updates.");
                _latestLauncherInfo = Downloader.DownloadAndDeserializeJsonData<LatestLauncherVersion>("https://files.veloe.link/launcher/update/versions.json");
                if (_latestLauncherInfo is not null && _latestLauncherInfo.Latest is not null)
                {
                    _logger.Information("Launcher version on server: {0}", _latestLauncherInfo.Latest);
                    _logger.Information("Launcher version: {0}", Assembly.GetExecutingAssembly().GetName().Version);

                    if (new Version(_latestLauncherInfo.Latest) > Assembly.GetExecutingAssembly().GetName().Version)
                    {
                        _logger.Debug("Update available!");
                        IsUpdateAvailable = true;
                    }
                }
                else
                    _logger.Warning("Can't get latest verion info! Skipping...");
            }
            catch (Exception ex)
            {
                OpenErrorWindow(ex);
            }
            try
            {
                var changelog = Downloader.DownloadAndDeserializeJsonData<List<Changelog>>("https://files.veloe.link/launcher/changelog.json");

                if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
                {
                    Dispatcher.UIThread.InvokeAsync(() =>
                    {
                        var stackpanel = desktop.MainWindow.GetControl<StackPanel>("ChangeLogStackPanel");
                        foreach (var version in changelog)
                        {
                            if (version.Title is not null)
                            {
                                stackpanel.Children.Add(new TextBlock()
                                {
                                    Text = version.Title,
                                    VerticalAlignment = Avalonia.Layout.VerticalAlignment.Stretch,
                                    TextWrapping = TextWrapping.Wrap,
                                    FontSize = 16
                                });
                            }
                            stackpanel.Children.Add(new TextBlock()
                            {
                                Text = version.Text,
                                VerticalAlignment = Avalonia.Layout.VerticalAlignment.Stretch,
                                TextWrapping = TextWrapping.Wrap
                            });
                        }
                    });

                }
            }
            catch (Exception ex)
            {
                OpenErrorWindow(ex);
            }
            try
            {
              
                _logger.Debug("Connecting to WebSoket");
                    //conection to my servers 

                var serverNames = Downloader.DownloadAndDeserializeJsonData<List<string>>("https://files.veloe.link/launcher/servers.json");

                _connection = new HubConnectionBuilder()
                    .WithUrl("https://monitor.veloe.link/hubs/data")
                    .WithAutomaticReconnect()
                    .Build();
            
                Func<Exception, Task> reconnecting = ex => Task.Run(() =>
                {
                    _logger.Warning("Reconnecting to WebCoket...");
                });
                Func<string, Task> reconnected = str => Task.Run(() =>
                {
                    _logger.Warning("Reconnected to WebCoket.");
                    foreach (var server in serverNames)
                    {
                        _connection.InvokeAsync("ConnectToGroup", server);
                    }
                });

                _connection.Reconnecting += reconnecting;
                _connection.Reconnected += reconnected;

                if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
                {
                    Dispatcher.UIThread.InvokeAsync(() =>
                    {
                        var stackpanel = desktop.MainWindow.GetControl<StackPanel>("ServersStackPanel");
                        foreach (var server in serverNames)
                            stackpanel.Children.Add(CreateServerPanel(server));
                    });

                }

                _connection.StartAsync();

                foreach (var server in serverNames)
                {
                    _connection.InvokeAsync("ConnectToGroup", server);
                }
                _logger.Debug("Connected to WebSoket");

                _consoleOutputTimer.Elapsed += UpdateConsoleOutput;
                _consoleOutputTimer.AutoReset = false;
            }
            catch (Exception ex)
            {
                OpenErrorWindow(ex);
            }
            try
            {
                _logger.Debug("Checking modpacks updates...");
                var modpacksInfo = Downloader.DownloadAndDeserializeJsonData<List<Modpack>>("https://files.veloe.link/launcher/modpacks.json");
                var installedModpacks = //DownloadedVersions.Where(v => modpacksInfo.Select(m => m.Name).Contains(v.version)).Join(modpacksInfo, (v, m) => v.version == m.);
                    from downloadedVersion in DownloadedVersions
                    join modpack in modpacksInfo on downloadedVersion.version equals modpack.Name
                    select new
                    {
                        Name = downloadedVersion.version,
                        Path = downloadedVersion.path,
                        Revision = modpack.Revision
                    };
                var modpacksToUpdate = new List<string>();
                var modpacksRevsionUnknown = new List<string>();

                foreach (var modpack in installedModpacks)
                {
                    if (File.Exists(Path.GetDirectoryName(modpack.Path) + "/revision.json"))
                    {
                        try
                        {
                            if (modpack.Revision > JsonSerializer.Deserialize<int>(File.ReadAllText(Path.GetDirectoryName(modpack.Path) + "/revision.json")))
                                modpacksToUpdate.Add(modpack.Name);
                        }
                        catch (Exception)
                        {
                            modpacksRevsionUnknown.Add(modpack.Name);
                        }
                    }
                    else
                        modpacksRevsionUnknown.Add(modpack.Name);
                }
                var message = string.Empty;
                if (modpacksToUpdate.Any())
                    message = $"Found updates for selected installed modpacks:{Environment.NewLine}    {string.Join($"{Environment.NewLine}    ", modpacksToUpdate)}";

                if (modpacksRevsionUnknown.Any())
                {
                    if (!string.IsNullOrEmpty(message))
                        message += Environment.NewLine;
                    message += $"Can't get revision for selected installed modpacks:{Environment.NewLine}    {string.Join($"{Environment.NewLine}    ", modpacksRevsionUnknown)}{Environment.NewLine}Please update them for correct verion checking!";
                }

                if (!string.IsNullOrEmpty(message))
                    OpenErrorWindow(message);

                _logger.Debug("Checking modpacks updates finished successfully!");
            }
            catch (Exception ex)
            {
                OpenErrorWindow(ex);
            }
        });
        
    }

    System.Timers.Timer _consoleOutputTimer = new(250);

    private HubConnection _connection;
    private string _downloadButton = "Download versions";
    private string _startButton = "Start Minecraft";
    private string _username = "";
    private StringBuilder _consoleText = new StringBuilder();
    private int _downloadedIndex;
    private string _argumentsBox;
    private bool _isNoGameRunning = true;
    private bool _isUpdateAvailable = false;
    private string _settingsButton = "Settings";
    private string _startButtonOutput;

    ILogger _logger;

    LatestLauncherVersion _latestLauncherInfo;
    DownloadedVersion _downloadedVersion;
    DownloadedVersion _startedVersion;

    ObservableCollection<DownloadedVersion> _downloadedVersions;

    public ObservableCollection<DownloadedVersion> DownloadedVersions { 
        get => _downloadedVersions; 
        set
        {
            this.RaiseAndSetIfChanged(ref _downloadedVersions, value);
        }
    }

    public DownloadedVersion DownloadedVersion
    {
        get => _downloadedVersion;
        set
        {
            this.RaiseAndSetIfChanged(ref _downloadedVersion, value);
            //Settings.lastChosenVersion = value.version;
            //logger.Debug("Version choosen: {0}",value.version);
            //logger.Debug("Version json: {0}", value.path);
        }
    }

    public int DownloadedIndex
    {
        get => _downloadedIndex;
        set
        {
            this.RaiseAndSetIfChanged(ref _downloadedIndex, value);
            if (value >= 0 && value < DownloadedVersions.Count)
                DownloadedVersion = DownloadedVersions[value];
        }
    }
    public string Greeting => "Welcome to Cringe Launcher!";
    public string DownloadButton {
        get => _downloadButton;
        set => this.RaiseAndSetIfChanged(ref _downloadButton, value);
    }

    public string StartButton
    {
        get => _startButton;
        set => this.RaiseAndSetIfChanged(ref _startButton, value);
    }

    public string Username 
    { 
        get => _username;
        set
        {
            this.RaiseAndSetIfChanged(ref _username, value);
            this.RaisePropertyChanged(nameof(IsStartButtonEnabled));
        }
    }

    public string ConsoleText
    {
        get 
        {
            if (_consoleText.Length < UInt16.MaxValue)
                return _consoleText.ToString();
            else
                return _consoleText.ToString(_consoleText.Length - UInt16.MaxValue, UInt16.MaxValue);
        }
        set
        {
            _consoleText.Clear();
            _consoleText.Append(value);
            this.RaisePropertyChanged(nameof(ConsoleText));
            ConsoleTextCaretIndex = int.MaxValue;
        }
    }

    public string StartButtonOutput
    {
        get => _startButtonOutput;
        set => this.RaiseAndSetIfChanged(ref _startButtonOutput, value);
    }

    public string ArgumentsBox
    {
        get => _argumentsBox;
        set => this.RaiseAndSetIfChanged(ref _argumentsBox, value);
    }

    public string SettingsButton
    {
        get => _settingsButton;
        set => this.RaiseAndSetIfChanged(ref _settingsButton, value);
    }

    public bool IsNoGameRunning
    {
        get => _isNoGameRunning;
        set
        {
            this.RaiseAndSetIfChanged(ref _isNoGameRunning, value);
            this.RaisePropertyChanged(nameof(IsStartButtonEnabled));
        }
    }

    public bool IsStartButtonEnabled
    {
        get => IsNoGameRunning && !string.IsNullOrEmpty(Username);
    }

    public bool IsUpdateAvailable
    {
        get => _isUpdateAvailable;
        set => this.RaiseAndSetIfChanged(ref _isUpdateAvailable, value);
    }

    public int ConsoleTextCaretIndex
    {
        get => int.MaxValue;
        set => this.RaisePropertyChanged(nameof(ConsoleTextCaretIndex));
    }

    public void OnClickCommand()
    {
        var versionsDownloader = new VersionsDownloader 
        { 
            DataContext = new VersionsDownloaderViewModel() 
        };
       
        if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
        {
            versionsDownloader.Closed += updateAvailable;
            versionsDownloader.ShowDialog(desktop.MainWindow);            
        }
    }

    public async void StartMinecraft()
    {
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
        Task.Run(async() =>
        {
            try
            {
                _logger.Debug("Starting minecraft.");
                if (DownloadedVersion is null)
                {
                    IsNoGameRunning = true;
                    return;
                }

                int version = 0;

                using (StreamReader reader = new StreamReader(DownloadedVersion.path))
                {
                    string json = reader.ReadToEnd();

                    var versionJson = JsonSerializer.Deserialize<Entity.Version.Version>(json, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });

                    if (Settings.checkGameAssets)
                    {
                        TaskStatus result;

                        if (versionJson.InheritsFrom is null)
                            result = Downloader.StartDownload(value => StartButtonOutput = value, value => IsNoGameRunning = value, value => { }, value => { }, versionJson).Result;
                        else
                        {
                            using (StreamReader inheritsFromReader = new StreamReader(Settings.minecraftForlderPath + "versions/" + versionJson.InheritsFrom + "/" + versionJson.InheritsFrom + ".json"))
                            {
                                string jsonInheritsFrom = inheritsFromReader.ReadToEnd();
                                var inheritsFromJson = JsonSerializer.Deserialize<Entity.Version.Version>(jsonInheritsFrom, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });

                                result = Downloader.StartDownload(value => StartButtonOutput = value, value => IsNoGameRunning = value, value => { }, value => { }, inheritsFromJson).Result;
                            }
                        }

                        if (result != TaskStatus.RanToCompletion)
                        {
                            IsNoGameRunning = true;
                            return;
                        }
                    }

                    string arguments = StartCommandBuilder.Build(versionJson, Username);

                    if (!Settings.useCustomJava)
                    {
                        if (versionJson.JavaVersion is not null)
                        {
                            _logger.Debug("Java version required: {0}", versionJson.JavaVersion.MajorVersion);
                            version = versionJson.JavaVersion.MajorVersion;
                        }
                        else
                        {
                            if (versionJson.InheritsFrom is not null)
                            {
                                using (StreamReader inheritsFromReader = new StreamReader(Settings.minecraftForlderPath + "versions/" + versionJson.InheritsFrom + "/" + versionJson.InheritsFrom + ".json"))
                                {
                                    string jsonInheritsFrom = inheritsFromReader.ReadToEnd();
                                    var inheritsFromJson = JsonSerializer.Deserialize<Entity.Version.Version>(jsonInheritsFrom, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
                                    if (inheritsFromJson.JavaVersion != null)
                                    {
                                        _logger.Debug("Java version required: {0}", inheritsFromJson.JavaVersion.MajorVersion);
                                        version = inheritsFromJson.JavaVersion.MajorVersion;
                                    }
                                }
                            }
                            else
                            {
                                version = 8;
                            }
                        }
                    }

                    //ConsoleText += arguments;
                    ArgumentsBox = arguments;
                }

                if (DownloadedVersion is null)
                    return;

                string javaPath = Settings.javaPath;

                if (!Settings.useCustomJava)
                {
                    javaPath = Path.GetFullPath(Settings.minecraftForlderPath + "javaruntime/" + version + "/bin/java");
                    if (OperatingSystem.IsWindows())
                        javaPath += ".exe";
                }
                else
                {
                    javaPath = Path.Combine(Settings.javaPath, "bin/java");
                    if (OperatingSystem.IsWindows())
                        javaPath += ".exe";
                }

                _logger.Debug("Java version path: {0}", Path.Combine(Settings.minecraftForlderPath, javaPath));
                _logger.Debug("Minecraft arguments: {0}", ArgumentsBox);

                ProcessStartInfo proc;
                proc = new ProcessStartInfo
                {
                    UseShellExecute = false,
                    RedirectStandardOutput = true,
                    RedirectStandardError = true,
                    WindowStyle = ProcessWindowStyle.Hidden,
                    CreateNoWindow = true,
                    FileName = Path.GetFullPath(Path.Combine(Settings.minecraftForlderPath, javaPath)),
                    StandardErrorEncoding = Encoding.UTF8,
                    WorkingDirectory = Path.GetDirectoryName(Path.Combine(Settings.minecraftForlderPath, javaPath)),
                    Arguments = ArgumentsBox
                };

                Process minecraft = new Process();

                minecraft.StartInfo = proc;

                if (!Settings.useCustomJava)
                    minecraft.StartInfo.EnvironmentVariables["JAVA_HOME"] = $"{Settings.minecraftForlderPath}javaruntime/{version}";
                else
                    minecraft.StartInfo.EnvironmentVariables["JAVA_HOME"] = Directory.GetParent(Path.GetDirectoryName(Settings.javaPath)).ToString();
                minecraft.StartInfo.EnvironmentVariables["PATH"] = "%JAVA_HOME%\bin;%PATH%";

                Task.Run(() =>
                {
                    try
                    {
                        _logger.Debug("Starting java process.");

                        minecraft.OutputDataReceived += OutputHandler;
                        minecraft.ErrorDataReceived += OutputHandler;

                        //* Start process and handlers            
                        //minecraft.WaitForExit();

                        minecraft.EnableRaisingEvents = true;

                        IsNoGameRunning = false;
                        _startedVersion = DownloadedVersion;
                        minecraft.Start();

                        minecraft.Exited += ProcessExited;

                        minecraft.BeginOutputReadLine();
                        minecraft.BeginErrorReadLine();

                        if (!Settings.gameLogToLauncher)
                        {
                            minecraft.OutputDataReceived -= OutputHandler;
                            minecraft.CancelOutputRead();
                        }

                        return Task.CompletedTask;
                    }
                    catch (Exception ex)
                    {
                        IsNoGameRunning = true;
                        OpenErrorWindow(ex);
                        return Task.CompletedTask;
                    }
                });
                _logger.Debug("Updating Settings");
                Settings.username = _username;
                Settings.lastChosenVersion = DownloadedVersion.version;
                Settings.SaveSettings();

            }
            catch (Exception ex)
            {              
                OpenErrorWindow(ex);
            }
        });
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
    }

    void OutputHandler(object sendingProcess, DataReceivedEventArgs outLine)
    {
        //Todo create multiple TextBlocks if char limit
        if (!_consoleOutputTimer.Enabled)
        {
            _consoleOutputTimer.Stop();
            _consoleOutputTimer.Start();
        }
        _consoleText.Append(outLine.Data + "\n");          
    }

    void LogHandler(object? sendingProcess, EventArgs args)
    {
        if (!_consoleOutputTimer.Enabled)
        {
            _consoleOutputTimer.Stop();
            _consoleOutputTimer.Start();
        }
        _consoleText.Append(((MyEventArgs)args).Data);
    }

    void UpdateConsoleOutput(object? source, ElapsedEventArgs e)
    {
        this.RaisePropertyChanged(nameof(ConsoleText));

        ScrollToEnd("ConsoleScroll");
    }

    void ScrollToEnd(string ScrollName)
    {
        if (Avalonia.Application.Current is not null && Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
        {
            Dispatcher.UIThread.InvokeAsync(() =>
            {
                var scroll = desktop.MainWindow.GetControl<ScrollViewer>("ConsoleScroll");
                scroll.ScrollToEnd();
            });

        }
    }

    void ProcessExited(object? sendingProcess, EventArgs e)
    {
        ((Process)sendingProcess).Exited -= ProcessExited;
        ((Process)sendingProcess).OutputDataReceived -= OutputHandler;
        ((Process)sendingProcess).ErrorDataReceived -= OutputHandler;
        ((Process)sendingProcess).CancelOutputRead();
        ((Process)sendingProcess).CancelErrorRead();

        _logger.Debug("Exit code: {0}", ((Process)sendingProcess).ExitCode);
       
        StartButtonOutput = "";
        IsNoGameRunning = true;
        if (((Process)sendingProcess).ExitCode is not (0 or 2))
            OpenErrorWindow(new JavaProcessException($"Minecraft process exited with an error code {((Process)sendingProcess).ExitCode}.\nCheck log in game folder or launcher console.", $"{Settings.minecraftForlderPath}versions/{_startedVersion.version}/crash-reports"));
        else if (((Process)sendingProcess).ExitCode is 2)
        {
            OpenErrorWindow(new Exception("JVM exited on the startup (Exit code 2). Check your Java installation. Get more info in the laucher console."));
        }
            

        ((Process)sendingProcess).Dispose();         
    }



    public void updateAvailable()
    {
        if (DownloadedVersions is null)
            DownloadedVersions = new();

        DownloadedVersions.Clear();
        DirectoryInfo versions = new( Settings.minecraftForlderPath + "versions");
        try
        {
            var dirs = versions.GetDirectories("*", SearchOption.TopDirectoryOnly);
            LauncherProfiles profiles = new LauncherProfiles();
            profiles.SelectedProfile = "NotImplemented";

            foreach (var dir in dirs)
            {
                _logger.Debug("Checking folder {0}", dir.Name);
                string checkedPath;
                if (File.Exists(checkedPath = dir.FullName + "/" + dir.Name + ".json"))
                {
                    _logger.Debug("Found version {0}",dir.Name);
                    DownloadedVersions.Add(new DownloadedVersion() { path = checkedPath, version = dir.Name });
                    profiles.Profiles.Add($"Version {dir.Name}", new Profile() { Name = dir.Name, LastVersionId = dir.Name, LauncherVisibilityOnGameClose = "keep the launcher open" });
                }

            }

            if (Settings.lastChosenVersion != null)
            {
                DownloadedIndex = 0;
                for (int i = 0; i < DownloadedVersions.Count; i++)
                {
                    if (DownloadedVersions[i].version == Settings.lastChosenVersion)
                    {
                        DownloadedIndex = i;
                        break;
                    }
                }
                
            }
            if (!File.Exists(Settings.minecraftForlderPath + "launcher_profiles.json"))
            {
                var file = File.Create(Settings.minecraftForlderPath + "launcher_profiles.json");
                file.Close();
                file.Dispose();
            }
            var lpString = JsonSerializer.Serialize(profiles, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
            File.WriteAllText(Settings.minecraftForlderPath + "launcher_profiles.json", lpString);
        }
        catch (DirectoryNotFoundException ex)
        {
            Directory.CreateDirectory(Settings.minecraftForlderPath + "versions");
            return;
        }
        
    }

    public void updateAvailable(object? sendingObject, EventArgs e)
    {
        try
        {
            updateAvailable();
        }
        catch (Exception ex)
        {
            OpenErrorWindow(ex);                
        }

        switch (sendingObject)
        {
            case VersionsDownloader:
                ((VersionsDownloader)sendingObject).Closed -= updateAvailable;
                break;

            case SettingsWindow:
                ((SettingsWindow)sendingObject).Closed -= updateAvailable;
                break;
        }

    }

    public void OpenSettings()
    {
        var settingsWindow = new SettingsWindow { DataContext = new SettingsWindowViewModel() };

        if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
        {
            settingsWindow.Closed += updateAvailable;
            settingsWindow.ShowDialog(desktop.MainWindow);
        }
    }

    public void DownloadUpdate()
    {
        _logger.Debug("Started updater.exe");
        Process updater = new Process();
        updater.StartInfo.FileName = "Updater";
        if (OperatingSystem.IsWindows())
            updater.StartInfo.FileName += ".exe";

        if (!File.Exists(updater.StartInfo.FileName))
            UpdateUpdater();

        if (File.Exists(updater.StartInfo.FileName))
        {
            updater.Start();

            if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
            {
                desktop.MainWindow.Close();
            }
        }
        else
            OpenErrorWindow(new FileNotFoundException($"File {updater.StartInfo.FileName} not found!"));
    }

    private void UpdateUpdater()
    {
        try
        {
            string fileName = "Updater";
            if (OperatingSystem.IsWindows())
                fileName += ".exe";
            if (File.Exists(fileName))
            {
                FileVersionInfo fileVersionInfo = FileVersionInfo.GetVersionInfo(fileName);
                LatestLauncherVersion latestLauncherVersion = Downloader.DownloadAndDeserializeJsonData<LatestLauncherVersion>("https://files.veloe.link/launcher/update/versions.json");

                if (OperatingSystem.IsLinux())
                {
                    _logger.Information("Manual updates only.");
                    return;
                }

                if (latestLauncherVersion is not null)
                {
                    _logger.Information("Latest updater version on server: {0}", latestLauncherVersion.Updater);
                    _logger.Information("Updater version: {0}", fileVersionInfo.FileVersion);

                    if (!(new Version(latestLauncherVersion.Updater) > new Version(fileVersionInfo.FileVersion)))
                    {
                        _logger.Information($"No update for \"{fileName}\" required.");
                        return;
                    }
                }
            }
                           
            WebClient webClient = new WebClient();
            try
            {
                _logger.Information("Downloading updater.zip");
                string url = String.Empty;

                if (OperatingSystem.IsWindows())
                    url = "https://files.veloe.link/launcher/update/updater.zip";

                if (OperatingSystem.IsLinux())
                    url = "https://files.veloe.link/launcher/update/updater_linux-x64.zip";

                if (url == String.Empty)
                    return;

                webClient.DownloadFile(new System.Uri(url), "updater.zip");
            }
            catch (Exception ex)
            {
                _logger.Error("Error occured on getting updater.zip from the server.");
                throw;
            }
            webClient.Dispose();
            _logger.Information("Unpacking updater.zip");
            ZipFile.ExtractToDirectory($"updater.zip", Directory.GetCurrentDirectory(), true);

            _logger.Information("Deleting updater.zip");
            File.Delete($"updater.zip");
            
        }
        catch (Exception ex)
        {
            OpenErrorWindow(ex);
        }

    }

    private Panel CreateServerPanel(string name)
    {
        ServerPanelModel serverPanelModel = new() { Name = name , Status = "Wait for update...", Tip = "No players.", Players = string.Empty};

        Panel panel = new Panel() 
        { 
            VerticalAlignment = Avalonia.Layout.VerticalAlignment.Top, 
            HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Stretch, 
            Height = 75, 
            Width = 150, 
            Margin = Avalonia.Thickness.Parse("0 0 10 10")
        };

        panel.Children.Add(new Border() 
        { 
            Background = Brushes.Black, 
            Opacity = 0.2, 
            CornerRadius = Avalonia.CornerRadius.Parse("15") 
        });

        var tip = new ToolTip()
        {
            Content = new TextBlock() {TextWrapping = TextWrapping.Wrap, [!TextBlock.TextProperty] = new Binding { Source = serverPanelModel,Path = nameof(serverPanelModel.Tip) } }
        };

        ToolTip.SetTip(panel, tip);

        StackPanel textPanel = new() { VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center, HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Center };

        textPanel.Children.Add(new TextBlock() { VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center, HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Center, [!TextBlock.TextProperty] = new Binding { Source = serverPanelModel, Path = nameof(serverPanelModel.Status) } });
        textPanel.Children.Add(new TextBlock() { FontSize = 20, VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center, HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Center, [!TextBlock.TextProperty] = new Binding { Source = serverPanelModel, Path = nameof(serverPanelModel.Players) } });
        
        panel.Children.Add(textPanel);

        System.Timers.Timer timeoutTimer = new System.Timers.Timer(30000);

        timeoutTimer.Elapsed += (Object source, ElapsedEventArgs e) => 
        { 
            serverPanelModel.Status = $"{serverPanelModel.Name}: Offline"; 
            serverPanelModel.Players = string.Empty; 
        };
        timeoutTimer.Start();

        _connection.On<string>($"Update{serverPanelModel.Name}", (message) =>
        {
            McStatus status = JsonSerializer.Deserialize<McStatus>(message, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
            serverPanelModel.Status = $"{serverPanelModel.Name}: Online";
            serverPanelModel.Players = $"{status.NumPlayers}/{status.MaxPlayers}";
            serverPanelModel.Tip = String.Empty;
            if (UInt16.Parse(status.NumPlayers) > 0)
                foreach (var player in status.Players)
                {
                    serverPanelModel.Tip += player;
                    serverPanelModel.Tip += "\n";
                }
            else
                serverPanelModel.Tip = "No players.";
            timeoutTimer.Stop();
            timeoutTimer.Start();
        });

        return panel;
    }
}
