﻿// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation 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.Generic;
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.PickMembers;
using Microsoft.VisualStudio.Language.Intellisense;
using Microsoft.VisualStudio.LanguageServices.Implementation.Utilities;
using Microsoft.VisualStudio.LanguageServices.Utilities;
using Roslyn.Utilities;

namespace Microsoft.VisualStudio.LanguageServices.Implementation.PickMembers
{
    internal class PickMembersDialogViewModel : AbstractNotifyPropertyChanged
    {
        private readonly List<MemberSymbolViewModel> _allMembers;

        public List<MemberSymbolViewModel> MemberContainers { get; set; }
        public List<OptionViewModel> Options { get; set; }

        /// <summary>
        /// <see langword="true"/> if 'Select All' was chosen.  <see langword="false"/> if 'Deselect All' was chosen.
        /// </summary>
        public bool SelectedAll { get; set; }

        internal PickMembersDialogViewModel(
            IGlyphService glyphService,
            ImmutableArray<ISymbol> members,
            ImmutableArray<PickMembersOption> options,
            bool selectAll)
        {
            _allMembers = members.Select(m => new MemberSymbolViewModel(m, glyphService)).ToList();
            MemberContainers = _allMembers;
            Options = options.Select(o => new OptionViewModel(o)).ToList();

            if (selectAll)
            {
                SelectAll();
            }
            else
            {
                DeselectAll();
            }
        }

        internal void Filter(string searchText)
        {
            searchText = searchText.Trim();
            MemberContainers = searchText.Length == 0
                ? _allMembers
                : _allMembers.Where(m => m.SymbolAutomationText.IndexOf(searchText, StringComparison.OrdinalIgnoreCase) >= 0).ToList();
            NotifyPropertyChanged(nameof(MemberContainers));
        }

        internal void DeselectAll()
        {
            SelectedAll = false;
            foreach (var memberContainer in MemberContainers)
                memberContainer.IsChecked = false;
        }

        internal void SelectAll()
        {
            SelectedAll = true;
            foreach (var memberContainer in MemberContainers)
                memberContainer.IsChecked = true;
        }

        private int? _selectedIndex;

        public int? SelectedIndex
        {
            get
            {
                return _selectedIndex;
            }

            set
            {
                var newSelectedIndex = value == -1 ? null : value;
                if (newSelectedIndex == _selectedIndex)
                {
                    return;
                }

                _selectedIndex = newSelectedIndex;

                NotifyPropertyChanged(nameof(CanMoveUp));
                NotifyPropertyChanged(nameof(MoveUpAutomationText));
                NotifyPropertyChanged(nameof(CanMoveDown));
                NotifyPropertyChanged(nameof(MoveDownAutomationText));
            }
        }

        public string MoveUpAutomationText
        {
            get
            {
                if (!CanMoveUp)
                {
                    return string.Empty;
                }

                return string.Format(ServicesVSResources.Move_0_above_1, MemberContainers[SelectedIndex.Value].SymbolAutomationText, MemberContainers[SelectedIndex.Value - 1].SymbolAutomationText);
            }
        }

        public string MoveDownAutomationText
        {
            get
            {
                if (!CanMoveDown)
                {
                    return string.Empty;
                }

                return string.Format(ServicesVSResources.Move_0_below_1, MemberContainers[SelectedIndex.Value].SymbolAutomationText, MemberContainers[SelectedIndex.Value + 1].SymbolAutomationText);
            }
        }

        [MemberNotNullWhen(true, nameof(SelectedIndex))]
        public bool CanMoveUp
        {
            get
            {
                if (!SelectedIndex.HasValue)
                {
                    return false;
                }

                var index = SelectedIndex.Value;
                return index > 0;
            }
        }

        [MemberNotNullWhen(true, nameof(SelectedIndex))]
        public bool CanMoveDown
        {
            get
            {
                if (!SelectedIndex.HasValue)
                {
                    return false;
                }

                var index = SelectedIndex.Value;
                return index < MemberContainers.Count - 1;
            }
        }

        internal void MoveUp()
        {
            Contract.ThrowIfFalse(CanMoveUp);

            var index = SelectedIndex.Value;
            Move(MemberContainers, index, delta: -1);
        }

        internal void MoveDown()
        {
            Contract.ThrowIfFalse(CanMoveDown);

            var index = SelectedIndex.Value;
            Move(MemberContainers, index, delta: 1);
        }

        private void Move(List<MemberSymbolViewModel> list, int index, int delta)
        {
            var param = list[index];
            list.RemoveAt(index);
            list.Insert(index + delta, param);

            SelectedIndex += delta;
        }

        internal class OptionViewModel : AbstractNotifyPropertyChanged
        {
            public PickMembersOption Option { get; }

            public string Title { get; }

            public OptionViewModel(PickMembersOption option)
            {
                Option = option;
                Title = option.Title;
                IsChecked = option.Value;
            }

            private bool _isChecked;
            public bool IsChecked
            {
                get => _isChecked;

                set
                {
                    Option.Value = value;
                    SetProperty(ref _isChecked, value);
                }
            }

            public string MemberAutomationText => Option.Title;
        }
    }
}
