// 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.Collections.Immutable;
using System.Runtime.CompilerServices;

namespace Microsoft.CodeAnalysis.CodeGeneration
{
    internal class CodeGenerationConstructorInfo
    {
        private static readonly ConditionalWeakTable<IMethodSymbol, CodeGenerationConstructorInfo> s_constructorToInfoMap = new();

        private readonly bool _isPrimaryConstructor;
        private readonly bool _isUnsafe;
        private readonly string _typeName;
        private readonly ImmutableArray<SyntaxNode> _baseConstructorArguments;
        private readonly ImmutableArray<SyntaxNode> _thisConstructorArguments;
        private readonly ImmutableArray<SyntaxNode> _statements;

        private CodeGenerationConstructorInfo(
            bool isPrimaryConstructor,
            bool isUnsafe,
            string typeName,
            ImmutableArray<SyntaxNode> statements,
            ImmutableArray<SyntaxNode> baseConstructorArguments,
            ImmutableArray<SyntaxNode> thisConstructorArguments)
        {
            _isPrimaryConstructor = isPrimaryConstructor;
            _isUnsafe = isUnsafe;
            _typeName = typeName;
            _statements = statements;
            _baseConstructorArguments = baseConstructorArguments;
            _thisConstructorArguments = thisConstructorArguments;
        }

        public static void Attach(
            IMethodSymbol constructor,
            bool isPrimaryConstructor,
            bool isUnsafe,
            string typeName,
            ImmutableArray<SyntaxNode> statements,
            ImmutableArray<SyntaxNode> baseConstructorArguments,
            ImmutableArray<SyntaxNode> thisConstructorArguments)
        {
            var info = new CodeGenerationConstructorInfo(isPrimaryConstructor, isUnsafe, typeName, statements, baseConstructorArguments, thisConstructorArguments);
            s_constructorToInfoMap.Add(constructor, info);
        }

        private static CodeGenerationConstructorInfo? GetInfo(IMethodSymbol method)
        {
            s_constructorToInfoMap.TryGetValue(method, out var info);
            return info;
        }

        public static ImmutableArray<SyntaxNode> GetThisConstructorArgumentsOpt(IMethodSymbol constructor)
            => GetThisConstructorArgumentsOpt(GetInfo(constructor));

        public static ImmutableArray<SyntaxNode> GetBaseConstructorArgumentsOpt(IMethodSymbol constructor)
            => GetBaseConstructorArgumentsOpt(GetInfo(constructor));

        public static ImmutableArray<SyntaxNode> GetStatements(IMethodSymbol constructor)
            => GetStatements(GetInfo(constructor));

        public static string GetTypeName(IMethodSymbol constructor)
            => GetTypeName(GetInfo(constructor), constructor);

        public static bool GetIsUnsafe(IMethodSymbol constructor)
            => GetIsUnsafe(GetInfo(constructor));

        public static bool GetIsPrimaryConstructor(IMethodSymbol constructor)
            => GetIsPrimaryConstructor(GetInfo(constructor));

        private static ImmutableArray<SyntaxNode> GetThisConstructorArgumentsOpt(CodeGenerationConstructorInfo? info)
            => info?._thisConstructorArguments ?? default;

        private static ImmutableArray<SyntaxNode> GetBaseConstructorArgumentsOpt(CodeGenerationConstructorInfo? info)
            => info?._baseConstructorArguments ?? default;

        private static ImmutableArray<SyntaxNode> GetStatements(CodeGenerationConstructorInfo? info)
            => info?._statements ?? default;

        private static string GetTypeName(CodeGenerationConstructorInfo? info, IMethodSymbol constructor)
            => info == null ? constructor.ContainingType.Name : info._typeName;

        private static bool GetIsUnsafe(CodeGenerationConstructorInfo? info)
            => info?._isUnsafe ?? false;

        private static bool GetIsPrimaryConstructor(CodeGenerationConstructorInfo? info)
            => info?._isPrimaryConstructor ?? false;
    }
}
