using Avalonia.Controls.Primitives;
using Avalonia.Threading;
using Ryujinx.Ava.Input;
using Ryujinx.Input;
using Ryujinx.Input.Assigner;
using System;
using System.Threading.Tasks;

namespace Ryujinx.Ava.UI.Helpers
{
    internal class ButtonKeyAssigner
    {
        internal class ButtonAssignedEventArgs : EventArgs
        {
            public ToggleButton Button { get; }
            public Button? ButtonValue { get; }

            public ButtonAssignedEventArgs(ToggleButton button, Button? buttonValue)
            {
                Button = button;
                ButtonValue = buttonValue;
            }
        }

        public ToggleButton ToggledButton { get; set; }

        private bool _isWaitingForInput;
        private bool _shouldUnbind;
        private IKeyboard _keyboard;
        public event EventHandler<ButtonAssignedEventArgs> ButtonAssigned;

        public ButtonKeyAssigner(ToggleButton toggleButton)
        {
            ToggledButton = toggleButton;
        }

        public async void GetInputAndAssign(IButtonAssigner assigner, IKeyboard keyboard = null)
        {
            _keyboard = keyboard;
            ClearKeyboardState(_keyboard);

            Dispatcher.UIThread.Post(() =>
            {
                ToggledButton.IsChecked = true;
            });

            if (_isWaitingForInput)
            {
                Dispatcher.UIThread.Post(() => Cancel());
                return;
            }

            _isWaitingForInput = true;

            assigner.Initialize();

            await Task.Run(async () =>
            {
                while (true)
                {
                    if (!_isWaitingForInput)
                    {
                        return;
                    }

                    await Task.Delay(10);

                    assigner.ReadInput();

                    if (assigner.IsAnyButtonPressed() || assigner.ShouldCancel() || (keyboard != null && keyboard.IsPressed(Key.Escape)))
                    {
                        break;
                    }
                }
            });

            await Dispatcher.UIThread.InvokeAsync(() =>
            {
                Button? pressedButton = assigner.GetPressedButton();

                if (_shouldUnbind)
                {
                    pressedButton = null;
                }

                _shouldUnbind = false;
                _isWaitingForInput = false;

                ToggledButton.IsChecked = false;
                ClearKeyboardState(_keyboard);

                if (pressedButton.HasValue && pressedButton.Value.AsHidType<Key>() == Key.BackSpace)
                {
                    ButtonAssigned?.Invoke(this, new ButtonAssignedEventArgs(ToggledButton, new Button(Key.Unbound)));
                    return;
                }

                ButtonAssigned?.Invoke(this, new ButtonAssignedEventArgs(ToggledButton, pressedButton));
            });
        }

        public void Cancel(bool shouldUnbind = false)
        {
            _isWaitingForInput = false;
            ToggledButton.IsChecked = false;
            _shouldUnbind = shouldUnbind;
            ClearKeyboardState(_keyboard);
        }

        private static void ClearKeyboardState(IKeyboard keyboard)
        {
            if (keyboard is AvaloniaKeyboard avaloniaKeyboard)
            {
                avaloniaKeyboard.Clear();
            }
        }
    }
}
