using System.Runtime.CompilerServices;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using SharpGen.Model;
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;

namespace SharpGen.Generator.Marshallers;

internal sealed class RefWrapperMarshaller : WrapperMarshallerBase
{
    public RefWrapperMarshaller(Ioc ioc, IMarshaller implementation) : base(ioc, implementation)
    {
    }

    public static bool IsApplicable(CsMarshalBase csElement) =>
        csElement is CsParameter {IsLocalManagedReference: true, IsArray: false, IsOptional: true};

    public override StatementSyntax GenerateManagedToNative(CsMarshalBase csElement, bool singleStackFrame) =>
        GenerateManagedToNativeForOptional(
            (CsParameter) csElement,
            base.GenerateManagedToNative(csElement, singleStackFrame)
        );

    private StatementSyntax GenerateManagedToNativeForOptional(CsParameter parameter, StatementSyntax statement)
    {
        var refIdentifier = IdentifierName(GetRefLocationIdentifier(parameter));

        StatementSyntaxList statements = new()
        {
            statement,
            IfStatement(
                BinaryExpression(
                    SyntaxKind.NotEqualsExpression,
                    InvocationExpression(
                        MemberAccessExpression(
                            SyntaxKind.SimpleMemberAccessExpression,
                            GlobalNamespace.GetTypeNameSyntax(BuiltinType.Unsafe),
                            IdentifierName(nameof(Unsafe.AsPointer))
                        ),
                        ArgumentList(
                            SingletonSeparatedList(
                                Argument(refIdentifier).WithRefOrOutKeyword(Token(SyntaxKind.RefKeyword))
                            )
                        )
                    ),
                    DefaultLiteral
                ),
                ExpressionStatement(
                    AssignmentExpression(
                        SyntaxKind.SimpleAssignmentExpression, refIdentifier,
                        GeneratesMarshalVariable(parameter)
                            ? GetMarshalStorageLocation(parameter)
                            : IdentifierName(parameter.Name)
                    )
                )
            )
        };

        return statements.ToStatement();
    }
}