using Avalonia;
using Avalonia.Controls;
using Avalonia.Layout;
using Avalonia.Threading;
using CommunityToolkit.Mvvm.Input;
using Gommon;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Ava.UI.Windows;
using Ryujinx.Ava.Utilities;
using Ryujinx.Ava.Utilities.Compat;
using Ryujinx.Ava.Utilities.Configuration;
using Ryujinx.Common;
using Ryujinx.Common.Helper;
using Ryujinx.Common.Utilities;
using Ryujinx.HLE.HOS.Services.Nfc.AmiiboDecryption;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Ryujinx.Ava.UI.Views.Main
{
    public partial class MainMenuBarView : UserControl
    {
        public MainWindow Window { get; private set; }
        public MainWindowViewModel ViewModel { get; private set; }

        public MainMenuBarView()
        {
            InitializeComponent();

            RyuLogo.IsVisible = !ConfigurationState.Instance.ShowTitleBar;
            RyuLogo.Source = MainWindowViewModel.IconBitmap;

            ToggleFileTypesMenuItem.ItemsSource = GenerateToggleFileTypeItems();
            ChangeLanguageMenuItem.ItemsSource = GenerateLanguageMenuItems();

            MiiAppletMenuItem.Command = new AsyncRelayCommand(OpenMiiApplet);
            CloseRyujinxMenuItem.Command = new RelayCommand(CloseWindow);
            OpenSettingsMenuItem.Command = new AsyncRelayCommand(OpenSettings);
            PauseEmulationMenuItem.Command = new RelayCommand(() => ViewModel.AppHost?.Pause());
            ResumeEmulationMenuItem.Command = new RelayCommand(() => ViewModel.AppHost?.Resume());
            StopEmulationMenuItem.Command = new AsyncRelayCommand(() => ViewModel.AppHost?.ShowExitPrompt().OrCompleted());
            CheatManagerMenuItem.Command = new AsyncRelayCommand(async () =>
            {
                try
                {
                    await OpenCheatManagerForCurrentApp();
                } catch {}
            });
            InstallFileTypesMenuItem.Command = new AsyncRelayCommand(InstallFileTypes);
            UninstallFileTypesMenuItem.Command = new AsyncRelayCommand(UninstallFileTypes);
            XciTrimmerMenuItem.Command = new AsyncRelayCommand(() => XCITrimmerWindow.Show(ViewModel));
            AboutWindowMenuItem.Command = new AsyncRelayCommand(AboutWindow.Show);
            CompatibilityListMenuItem.Command = new AsyncRelayCommand(CompatibilityList.Show);
            
            UpdateMenuItem.Command = new AsyncRelayCommand(async () =>
            {
                if (Updater.CanUpdate(true))
                    await Updater.BeginUpdateAsync(true);
            });

            FaqMenuItem.Command = 
                SetupGuideMenuItem.Command = 
                    LdnGuideMenuItem.Command = new RelayCommand<string>(OpenHelper.OpenUrl);
            
            WindowSize720PMenuItem.Command = 
                WindowSize1080PMenuItem.Command = 
                    WindowSize1440PMenuItem.Command = 
                        WindowSize2160PMenuItem.Command = new RelayCommand<string>(ChangeWindowSize);
        }

        private IEnumerable<CheckBox> GenerateToggleFileTypeItems() =>
            Enum.GetValues<FileTypes>()
                .Select(it => (FileName: Enum.GetName(it)!, FileType: it))
                .Select(it =>
                    new CheckBox
                    {
                        Content = $".{it.FileName}",
                        IsChecked = it.FileType.GetConfigValue(ConfigurationState.Instance.UI.ShownFileTypes),
                        Command = MiniCommand.Create(() => Window.ToggleFileType(it.FileName))
                    }
                );

        private static IEnumerable<MenuItem> GenerateLanguageMenuItems()
        {
            const string LocalePath = "Ryujinx/Assets/locales.json";

            string languageJson = EmbeddedResources.ReadAllText(LocalePath);

            LocalesJson locales = JsonHelper.Deserialize(languageJson, LocalesJsonContext.Default.LocalesJson);

            foreach (string language in locales.Languages)
            {
                int index = locales.Locales.FindIndex(x => x.ID == "Language");
                string languageName;

                if (index == -1)
                {
                    languageName = language;
                }
                else
                {
                    string tr = locales.Locales[index].Translations[language];
                    languageName = string.IsNullOrEmpty(tr)
                        ? language 
                        : tr;
                }

                MenuItem menuItem = new()
                {
                    Padding = new Thickness(15, 0, 0, 0),
                    Margin = new Thickness(3, 0, 3, 0),
                    HorizontalAlignment = HorizontalAlignment.Stretch,
                    Header = languageName,
                    Command = MiniCommand.Create(() => MainWindowViewModel.ChangeLanguage(language))
                };

                yield return menuItem;
            }
        }

        protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
        {
            base.OnAttachedToVisualTree(e);

            if (VisualRoot is MainWindow window)
            {
                Window = window;
                DataContext = ViewModel = window.ViewModel;
            }
        }

        public async Task OpenSettings()
        {
            Window.SettingsWindow = new(Window.VirtualFileSystem, Window.ContentManager);

            await Window.SettingsWindow.ShowDialog(Window);

            Window.SettingsWindow = null;

            ViewModel.LoadConfigurableHotKeys();
        }

        public static readonly AppletMetadata MiiApplet = new("miiEdit", 0x0100000000001009);

        public async Task OpenMiiApplet()
        {
            if (MiiApplet.CanStart(ViewModel.ContentManager, out var appData, out var nacpData))
            {
                await ViewModel.LoadApplication(appData, ViewModel.IsFullScreen || ViewModel.StartGamesInFullscreen, nacpData);
            }
        }

        public async Task OpenCheatManagerForCurrentApp()
        {
            if (!ViewModel.IsGameRunning)
                return;

            string name = ViewModel.AppHost.Device.Processes.ActiveApplication.ApplicationControlProperties.Title[(int)ViewModel.AppHost.Device.System.State.DesiredTitleLanguage].NameString.ToString();

            await new CheatWindow(
                Window.VirtualFileSystem,
                ViewModel.AppHost.Device.Processes.ActiveApplication.ProgramIdText,
                name,
                ViewModel.SelectedApplication.Path).ShowDialog(Window);

            ViewModel.AppHost.Device.EnableCheats();
        }

        private void ScanAmiiboMenuItem_AttachedToVisualTree(object sender, VisualTreeAttachmentEventArgs e)
        {
            if (sender is MenuItem)
                ViewModel.IsAmiiboRequested = ViewModel.AppHost.Device.System.SearchingForAmiibo(out _);
        }

        private void ScanBinAmiiboMenuItem_AttachedToVisualTree(object sender, VisualTreeAttachmentEventArgs e)
        {
            if (sender is MenuItem)
                ViewModel.IsAmiiboBinRequested = ViewModel.IsAmiiboRequested && AmiiboBinReader.HasAmiiboKeyFile;
        }

        private async Task InstallFileTypes()
        {
            ViewModel.AreMimeTypesRegistered = FileAssociationHelper.Install();
            if (ViewModel.AreMimeTypesRegistered)
                await ContentDialogHelper.CreateInfoDialog(LocaleManager.Instance[LocaleKeys.DialogInstallFileTypesSuccessMessage], string.Empty, LocaleManager.Instance[LocaleKeys.InputDialogOk], string.Empty, string.Empty);
            else
                await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogInstallFileTypesErrorMessage]);
        }

        private async Task UninstallFileTypes()
        {
            ViewModel.AreMimeTypesRegistered = !FileAssociationHelper.Uninstall();
            if (!ViewModel.AreMimeTypesRegistered)
                await ContentDialogHelper.CreateInfoDialog(LocaleManager.Instance[LocaleKeys.DialogUninstallFileTypesSuccessMessage], string.Empty, LocaleManager.Instance[LocaleKeys.InputDialogOk], string.Empty, string.Empty);
            else
                await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogUninstallFileTypesErrorMessage]);
        }

        private void ChangeWindowSize(string resolution)
        {
            (int resolutionWidth, int resolutionHeight) = resolution.Split(' ', 2)
                .Into(parts => 
                    (int.Parse(parts[0]), int.Parse(parts[1]))
                );

            // Correctly size window when 'TitleBar' is enabled (Nov. 14, 2024)
            double barsHeight = ((Window.StatusBarHeight + Window.MenuBarHeight) +
                (ConfigurationState.Instance.ShowTitleBar ? (int)Window.TitleBar.Height : 0));

            double windowWidthScaled = (resolutionWidth * Program.WindowScaleFactor);
            double windowHeightScaled = ((resolutionHeight + barsHeight) * Program.WindowScaleFactor);

            Dispatcher.UIThread.Post(() =>
            {
                ViewModel.WindowState = WindowState.Normal;

                Window.Arrange(new Rect(Window.Position.X, Window.Position.Y, windowWidthScaled, windowHeightScaled));
            });
        }

        public void CloseWindow() => Window.Close();
        
    }
}
