// 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.Diagnostics;
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis
{
    internal partial struct SymbolKey
    {
        private sealed class BuiltinOperatorSymbolKey : AbstractSymbolKey<IMethodSymbol>
        {
            public static readonly BuiltinOperatorSymbolKey Instance = new();

            public sealed override void Create(IMethodSymbol symbol, SymbolKeyWriter visitor)
            {
                visitor.WriteString(symbol.Name);
                visitor.WriteSymbolKey(symbol.ReturnType);
                visitor.WriteParameterTypesArray(symbol.Parameters);
            }

            protected sealed override SymbolKeyResolution Resolve(
                SymbolKeyReader reader, IMethodSymbol? contextualSymbol, out string? failureReason)
            {
                var name = reader.ReadRequiredString();

                var returnType = reader.ReadSymbolKey(contextualSymbol?.ReturnType, out var returnTypeFailureReason);
                using var parameterTypes = reader.ReadSymbolKeyArray<IMethodSymbol, ITypeSymbol>(
                    contextualSymbol,
                    static (contextualSymbol, i) => SafeGet(contextualSymbol.Parameters, i)?.Type,
                    out var parameterTypesFailureReason);

                if (returnTypeFailureReason != null)
                {
                    failureReason = $"({nameof(BuiltinOperatorSymbolKey)} {nameof(returnType)} failed -> {returnTypeFailureReason})";
                    return default;
                }

                if (parameterTypesFailureReason != null)
                {
                    failureReason = $"({nameof(BuiltinOperatorSymbolKey)} {nameof(parameterTypes)} failed -> {parameterTypesFailureReason})";
                    return default;
                }

                var returnTypeSymbol = (ITypeSymbol?)returnType.GetAnySymbol();
                Contract.ThrowIfNull(returnTypeSymbol);

                try
                {
                    switch (parameterTypes.Count)
                    {
                        case 1:
                            failureReason = null;
                            var unaryOperator = reader.Compilation.CreateBuiltinOperator(name, returnTypeSymbol, parameterTypes[0]);
                            return new SymbolKeyResolution(unaryOperator);
                        case 2:
                            failureReason = null;
                            var binaryOperator = reader.Compilation.CreateBuiltinOperator(name, returnTypeSymbol, parameterTypes[0], parameterTypes[1]);
                            return new SymbolKeyResolution(binaryOperator);

                        default:
                            failureReason = $"({nameof(BuiltinOperatorSymbolKey)} {nameof(parameterTypes)} failed -> count was {parameterTypes.Count})";
                            return default;
                    }
                }
                catch (ArgumentException ex)
                {
                    failureReason = $"({nameof(BuiltinOperatorSymbolKey)} failed -> {ex.Message})";
                    return default;
                }
            }
        }
    }
}
