// 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.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis.GenerateMember.GenerateParameterizedMember;

internal abstract partial class AbstractGenerateConversionService<TService, TSimpleNameSyntax, TExpressionSyntax, TInvocationExpressionSyntax>
{
    protected new sealed class State : AbstractGenerateParameterizedMemberService<TService, TSimpleNameSyntax, TExpressionSyntax, TInvocationExpressionSyntax>.State
    {
        public static async Task<State> GenerateConversionStateAsync(
           TService service,
           SemanticDocument document,
           SyntaxNode interfaceNode,
           CancellationToken cancellationToken)
        {
            var state = new State();
            if (!await state.TryInitializeConversionAsync(service, document, interfaceNode, cancellationToken).ConfigureAwait(false))
            {
                return null;
            }

            return state;
        }

        private async ValueTask<bool> TryInitializeConversionAsync(
            TService service,
            SemanticDocument document,
            SyntaxNode node,
            CancellationToken cancellationToken)
        {
            if (service.IsImplicitConversionGeneration(node))
            {
                if (!TryInitializeImplicitConversion(service, document, node, cancellationToken))
                {
                    return false;
                }
            }
            else if (service.IsExplicitConversionGeneration(node))
            {
                if (!TryInitializeExplicitConversion(service, document, node, cancellationToken))
                {
                    return false;
                }
            }

            return await TryFinishInitializingStateAsync(service, document, cancellationToken).ConfigureAwait(false);
        }

        private bool TryInitializeExplicitConversion(TService service, SemanticDocument document, SyntaxNode node, CancellationToken cancellationToken)
        {
            MethodKind = MethodKind.Conversion;
            if (!service.TryInitializeExplicitConversionState(
                document, node, ClassInterfaceModuleStructTypes, cancellationToken,
                out var identifierToken, out var methodSymbol, out var typeToGenerateIn))
            {
                return false;
            }

            ContainingType = document.SemanticModel.GetEnclosingNamedType(node.SpanStart, cancellationToken);
            if (ContainingType == null)
            {
                return false;
            }

            IdentifierToken = identifierToken;
            TypeToGenerateIn = typeToGenerateIn;
            SignatureInfo = new MethodSignatureInfo(document, this, methodSymbol);
            location = node.GetLocation();
            MethodGenerationKind = MethodGenerationKind.ExplicitConversion;
            return true;
        }

        private bool TryInitializeImplicitConversion(TService service, SemanticDocument document, SyntaxNode node, CancellationToken cancellationToken)
        {
            MethodKind = MethodKind.Conversion;
            if (!service.TryInitializeImplicitConversionState(
                document, node, ClassInterfaceModuleStructTypes, cancellationToken,
                out var identifierToken, out var methodSymbol, out var typeToGenerateIn))
            {
                return false;
            }

            ContainingType = document.SemanticModel.GetEnclosingNamedType(node.SpanStart, cancellationToken);
            if (ContainingType == null)
            {
                return false;
            }

            IdentifierToken = identifierToken;
            TypeToGenerateIn = typeToGenerateIn;
            SignatureInfo = new MethodSignatureInfo(document, this, methodSymbol);
            MethodGenerationKind = MethodGenerationKind.ImplicitConversion;
            return true;
        }
    }
}
