// 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;
using System.Diagnostics;
using Microsoft.VisualStudio.Debugger.ComponentInterfaces;
using Microsoft.VisualStudio.Debugger.Evaluation;
using Microsoft.VisualStudio.Debugger.Evaluation.ClrCompilation;

namespace Microsoft.CodeAnalysis.ExpressionEvaluator
{
    internal sealed class DynamicViewExpansion : Expansion
    {
        private const string DynamicFormatSpecifier = "dynamic";

        internal static DynamicViewExpansion CreateExpansion(DkmInspectionContext inspectionContext, DkmClrValue value)
        {
            if (value.IsError() || value.IsNull || value.HasExceptionThrown())
            {
                return null;
            }

            var type = value.Type.GetLmrType();
            if (!(type.IsComObject() || type.IsIDynamicMetaObjectProvider()))
            {
                return null;
            }

            var proxyValue = value.InstantiateDynamicViewProxy(inspectionContext);
            Debug.Assert((proxyValue == null) || (!proxyValue.IsNull && !proxyValue.IsError() && !proxyValue.HasExceptionThrown()));
            // InstantiateDynamicViewProxy may return null (if required assembly is missing, for instance).
            if (proxyValue == null)
            {
                return null;
            }

            // Expansion is based on the 'DynamicMetaObjectProviderDebugView.Items' property.
            var proxyType = proxyValue.Type;
            var itemsMemberExpansion = RootHiddenExpansion.CreateExpansion(
                proxyType.GetMemberByName("Items"),
                CustomTypeInfoTypeArgumentMap.Create(new TypeAndCustomInfo(proxyType)));
            return new DynamicViewExpansion(proxyValue, itemsMemberExpansion);
        }

        internal static EvalResult CreateMembersOnlyRow(
            DkmInspectionContext inspectionContext,
            string name,
            DkmClrValue value,
            ResultProvider resultProvider)
        {
            var expansion = CreateExpansion(inspectionContext, value);
            return (expansion != null)
                ? expansion.CreateDynamicViewRow(inspectionContext, name, parent: null, fullNameProvider: resultProvider.FullNameProvider)
                : new EvalResult(name, Resources.DynamicViewNotDynamic, inspectionContext);
        }

        private readonly DkmClrValue _proxyValue;
        private readonly Expansion _proxyMembers;

        private DynamicViewExpansion(DkmClrValue proxyValue, Expansion proxyMembers)
        {
            Debug.Assert(proxyValue != null);
            Debug.Assert(proxyMembers != null);

            _proxyValue = proxyValue;
            _proxyMembers = proxyMembers;
        }

        internal override void GetRows(
            ResultProvider resultProvider,
            ArrayBuilder<EvalResult> rows,
            DkmInspectionContext inspectionContext,
            EvalResultDataItem parent,
            DkmClrValue value,
            int startIndex,
            int count,
            bool visitAll,
            ref int index)
        {
            if (InRange(startIndex, count, index))
            {
                rows.Add(CreateDynamicViewRow(inspectionContext, Resources.DynamicView, parent, resultProvider.FullNameProvider));
            }

            index++;
        }

        private EvalResult CreateDynamicViewRow(DkmInspectionContext inspectionContext, string name, EvalResultDataItem parent, IDkmClrFullNameProvider fullNameProvider)
        {
            var proxyTypeAndInfo = new TypeAndCustomInfo(_proxyValue.Type);
            var isRootExpression = parent == null;
            var fullName = isRootExpression ? name : parent.ChildFullNamePrefix;
            var childFullNamePrefix = (fullName == null)
                ? null
                : fullNameProvider.GetClrObjectCreationExpression(
                    inspectionContext,
                    proxyTypeAndInfo.ClrType,
                    proxyTypeAndInfo.Info,
                    new[] { fullName });
            var formatSpecifiers = isRootExpression ? Formatter.NoFormatSpecifiers : parent.FormatSpecifiers;
            return new EvalResult(
                ExpansionKind.DynamicView,
                name,
                typeDeclaringMemberAndInfo: default(TypeAndCustomInfo),
                declaredTypeAndInfo: proxyTypeAndInfo,
                useDebuggerDisplay: false,
                value: _proxyValue,
                displayValue: Resources.DynamicViewValueWarning,
                expansion: _proxyMembers,
                childShouldParenthesize: false,
                fullName: fullName,
                childFullNamePrefixOpt: childFullNamePrefix,
                formatSpecifiers: Formatter.AddFormatSpecifier(formatSpecifiers, DynamicFormatSpecifier),
                category: DkmEvaluationResultCategory.Method,
                flags: DkmEvaluationResultFlags.ReadOnly,
                editableValue: null,
                inspectionContext: inspectionContext);
        }
    }
}
