﻿// 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.

#nullable disable

using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.PullMemberUp;
using Microsoft.VisualStudio.Language.Intellisense;
using Microsoft.VisualStudio.LanguageServices.Implementation.Utilities;

namespace Microsoft.VisualStudio.LanguageServices.Implementation.PullMemberUp.MainDialog
{
    /// <summary>
    /// View model used to represent and display the inheritance graph as a tree. This tree is constructed by breadth first searching.
    /// If one type is the common base type of several other types, it will be showed multiple time.
    /// </summary>
    internal class BaseTypeTreeNodeViewModel : SymbolViewModel<INamedTypeSymbol>
    {
        /// <summary>
        /// Base types of this tree node
        /// </summary>
        public ImmutableArray<BaseTypeTreeNodeViewModel> BaseTypeNodes { get; private set; }

        public bool IsExpanded { get; set; }

        /// <summary>
        /// Content of the tooltip.
        /// </summary>
        public string Namespace => string.Format(ServicesVSResources.Namespace_0, Symbol.ContainingNamespace?.ToDisplayString() ?? "global");

        private BaseTypeTreeNodeViewModel(INamedTypeSymbol node, IGlyphService glyphService) : base(node, glyphService)
        {
        }

        /// <summary>
        /// Use breadth first search to create the inheritance tree. Only non-generated types in the solution will be included in the tree.
        /// </summary>
        public static BaseTypeTreeNodeViewModel CreateBaseTypeTree(
            IGlyphService glyphService,
            Solution solution,
            INamedTypeSymbol root,
            CancellationToken cancellationToken)
        {
            var rootTreeNode = new BaseTypeTreeNodeViewModel(root, glyphService) { IsChecked = false, IsExpanded = true };
            var queue = new Queue<BaseTypeTreeNodeViewModel>();
            queue.Enqueue(rootTreeNode);
            while (queue.Any())
            {
                var currentTreeNode = queue.Dequeue();
                var currentTypeSymbol = currentTreeNode.Symbol;

                currentTreeNode.BaseTypeNodes = currentTypeSymbol.Interfaces
                    .Concat(currentTypeSymbol.BaseType)
                    .Where(baseType => baseType != null && MemberAndDestinationValidator.IsDestinationValid(solution, baseType, cancellationToken))
                    .OrderBy(baseType => baseType.ToDisplayString())
                    .Select(baseType => new BaseTypeTreeNodeViewModel(baseType, glyphService) { IsChecked = false, IsExpanded = true })
                    .ToImmutableArray();

                foreach (var node in currentTreeNode.BaseTypeNodes)
                {
                    queue.Enqueue(node);
                }
            }

            return rootTreeNode;
        }
    }
}
