// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Threading.Tasks;
using System.Windows.Input;
using System.Windows.Media;
using PowerLauncher.Helper;
using PowerLauncher.Plugin;
using Wox.Infrastructure.Image;
using Wox.Infrastructure.UserSettings;
using Wox.Plugin;
using Wox.Plugin.Logger;

namespace PowerLauncher.ViewModel
{
    public class ResultViewModel : BaseModel
    {
        public enum ActivationType
        {
            Selection,
            Hover,
        }

        private readonly PowerToysRunSettings _settings;

        public ObservableCollection<ContextMenuItemViewModel> ContextMenuItems { get; } = new ObservableCollection<ContextMenuItemViewModel>();

        public ICommand ActivateContextButtonsHoverCommand { get; }

        public ICommand DeactivateContextButtonsHoverCommand { get; }

        public bool IsSelected { get; private set; }

        public bool IsHovered { get; private set; }

        private bool _areContextButtonsActive;

        private ImageSource _image;
        private volatile bool _imageLoaded;

        public bool AreContextButtonsActive
        {
            get => _areContextButtonsActive;
            set
            {
                if (_areContextButtonsActive != value)
                {
                    _areContextButtonsActive = value;
                    OnPropertyChanged(nameof(AreContextButtonsActive));
                }
            }
        }

        private int _contextMenuSelectedIndex;

        public int ContextMenuSelectedIndex
        {
            get => _contextMenuSelectedIndex;
            set
            {
                if (_contextMenuSelectedIndex != value)
                {
                    _contextMenuSelectedIndex = value;
                    OnPropertyChanged(nameof(ContextMenuSelectedIndex));
                }
            }
        }

        public const int NoSelectionIndex = -1;

        public ResultViewModel(Result result, IMainViewModel mainViewModel, PowerToysRunSettings settings)
        {
            if (result != null)
            {
                Result = result;
            }

            _settings = settings;

            ContextMenuSelectedIndex = NoSelectionIndex;
            LoadContextMenu();

            ActivateContextButtonsHoverCommand = new RelayCommand(ActivateContextButtonsHoverAction);
            DeactivateContextButtonsHoverCommand = new RelayCommand(DeactivateContextButtonsHoverAction);
            MainViewModel = mainViewModel;
        }

        private void ActivateContextButtonsHoverAction(object sender)
        {
            ActivateContextButtons(ActivationType.Hover);
        }

        public void ActivateContextButtons(ActivationType activationType)
        {
            // Result does not contain any context menu items - we don't need to show the context menu ListView at all.
            if (ContextMenuItems.Count > 0)
            {
                AreContextButtonsActive = true;
            }
            else
            {
                AreContextButtonsActive = false;
            }

            if (activationType == ActivationType.Selection)
            {
                IsSelected = true;
                EnableContextMenuAcceleratorKeys();
            }
            else if (activationType == ActivationType.Hover)
            {
                IsHovered = true;
            }
        }

        private void DeactivateContextButtonsHoverAction(object sender)
        {
            DeactivateContextButtons(ActivationType.Hover);
        }

        public void DeactivateContextButtons(ActivationType activationType)
        {
            if (activationType == ActivationType.Selection)
            {
                IsSelected = false;
                DisableContextMenuAcceleratorkeys();
            }
            else if (activationType == ActivationType.Hover)
            {
                IsHovered = false;
            }

            // Result does not contain any context menu items - we don't need to show the context menu ListView at all.
            if (ContextMenuItems?.Count > 0)
            {
                AreContextButtonsActive = IsSelected || IsHovered;
            }
            else
            {
                AreContextButtonsActive = false;
            }
        }

        public void LoadContextMenu()
        {
            var results = PluginManager.GetContextMenusForPlugin(Result);
            ContextMenuItems.Clear();
            foreach (var r in results)
            {
                ContextMenuItems.Add(new ContextMenuItemViewModel(
                    r.PluginName,
                    r.Title,
                    r.Glyph,
                    r.FontFamily,
                    r.AcceleratorKey,
                    r.AcceleratorModifiers,
                    new RelayCommand(_ =>
                    {
                        bool hideWindow =
                            r.Action != null &&
                            r.Action(new ActionContext
                            {
                                SpecialKeyState = KeyboardHelper.CheckModifiers(),
                            });

                        if (hideWindow)
                        {
                            MainViewModel.Hide();
                        }
                    })));
            }
        }

        private void EnableContextMenuAcceleratorKeys()
        {
            foreach (var i in ContextMenuItems)
            {
                i.IsAcceleratorKeyEnabled = true;
            }
        }

        private void DisableContextMenuAcceleratorkeys()
        {
            foreach (var i in ContextMenuItems)
            {
                i.IsAcceleratorKeyEnabled = false;
            }
        }

        public ImageSource Image
        {
            get
            {
                if (!_imageLoaded)
                {
                    _imageLoaded = true;
                    _ = LoadImageAsync();
                }

                return _image;
            }

            private set
            {
                _image = value;
                OnPropertyChanged(nameof(Image));
            }
        }

        private async Task<ImageSource> LoadImageInternalAsync(string imagePath, Result.IconDelegate icon, bool loadFullImage)
        {
            if (string.IsNullOrEmpty(imagePath) && icon != null)
            {
                try
                {
                    var image = icon();
                    return image;
                }
                catch (Exception e)
                {
                    Log.Exception(
                        $"IcoPath is empty and exception when calling Icon() for result <{Result.Title}> of plugin <{Result.PluginDirectory}>",
                        e,
                        System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
                    imagePath = ImageLoader.ErrorIconPath;
                }
            }

            return await ImageLoader.LoadAsync(imagePath, _settings.GenerateThumbnailsFromFiles, loadFullImage).ConfigureAwait(false);
        }

        private async Task LoadImageAsync()
        {
            var imagePath = Result.IcoPath;
            var iconDelegate = Result.Icon;
            Image = await LoadImageInternalAsync(imagePath, iconDelegate, false).ConfigureAwait(false);
        }

        // Returns false if we've already reached the last item.
        public bool SelectNextContextButton()
        {
            if (ContextMenuSelectedIndex == (ContextMenuItems.Count - 1))
            {
                ContextMenuSelectedIndex = NoSelectionIndex;
                return false;
            }

            ContextMenuSelectedIndex++;
            return true;
        }

        // Returns false if we've already reached the first item.
        public bool SelectPrevContextButton()
        {
            if (ContextMenuSelectedIndex == NoSelectionIndex)
            {
                return false;
            }

            ContextMenuSelectedIndex--;
            return true;
        }

        public void SelectLastContextButton()
        {
            ContextMenuSelectedIndex = ContextMenuItems.Count - 1;
        }

        public bool HasSelectedContextButton()
        {
            var isContextSelected = ContextMenuSelectedIndex != NoSelectionIndex;
            return isContextSelected;
        }

        /// <summary>
        ///  Triggers the action on the selected context button
        /// </summary>
        /// <returns>False if there is nothing selected, otherwise true</returns>
        public bool ExecuteSelectedContextButton()
        {
            if (HasSelectedContextButton())
            {
                ContextMenuItems[ContextMenuSelectedIndex].Command.Execute(null);
                return true;
            }

            return false;
        }

        public Result Result { get; }

        public IMainViewModel MainViewModel { get; }

        public override bool Equals(object obj)
        {
            var r = obj as ResultViewModel;
            if (r != null)
            {
                return Result.Equals(r.Result);
            }
            else
            {
                return false;
            }
        }

        public override int GetHashCode()
        {
            return Result.GetHashCode();
        }

        public string SearchBoxDisplayText()
        {
            return Result.QueryTextDisplay;
        }

        public override string ToString()
        {
            // Using CurrentCulture since this is user facing
            var contextMenuInfo = ContextMenuItems.Count > 0 ? string.Format(CultureInfo.CurrentCulture, "{0} {1}", ContextMenuItems.Count, Properties.Resources.ContextMenuItemsAvailable) : string.Empty;
            return string.Format(CultureInfo.CurrentCulture, "{0}, {1}", Result.ToString(), contextMenuInfo);
        }
    }
}
