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

namespace Microsoft.CSharp.RuntimeBinder.Semantics
{
    internal readonly partial struct ExpressionBinder
    {
        private static bool IsNullableConstructor(Expr expr, out ExprCall call)
        {
            Debug.Assert(expr != null);

            if (expr is ExprCall pCall && pCall.MemberGroup.OptionalObject == null
                && (pCall.MethWithInst?.Meth().IsNullableConstructor() ?? false))
            {
                call = pCall;
                return true;
            }

            call = null;
            return false;
        }

        private static Expr StripNullableConstructor(Expr pExpr)
        {
            while (IsNullableConstructor(pExpr, out ExprCall call))
            {
                pExpr = call.OptionalArguments;
                Debug.Assert(pExpr != null && !(pExpr is ExprList));
            }

            return pExpr;
        }

        // Create an expr for exprSrc.Value where exprSrc.type is a NullableType.
        private static Expr BindNubValue(Expr exprSrc)
        {
            Debug.Assert(exprSrc != null && exprSrc.Type is NullableType);

            // For new T?(x), the answer is x.
            if (IsNullableConstructor(exprSrc, out ExprCall call))
            {
                Expr args = call.OptionalArguments;
                Debug.Assert(args != null && !(args is ExprList));
                return args;
            }

            NullableType nubSrc = (NullableType)exprSrc.Type;
            CType typeBase = nubSrc.UnderlyingType;
            AggregateType ats = nubSrc.GetAts();
            PropertySymbol prop = PredefinedMembers.GetProperty(PREDEFPROP.PP_G_OPTIONAL_VALUE);
            PropWithType pwt = new PropWithType(prop, ats);
            MethPropWithInst mpwi = new MethPropWithInst(prop, ats);
            ExprMemberGroup pMemGroup = ExprFactory.CreateMemGroup(exprSrc, mpwi);
            return ExprFactory.CreateProperty(typeBase, null, null, pMemGroup, pwt, null);
        }

        // Create an expr for new T?(exprSrc) where T is exprSrc.type.
        private static ExprCall BindNubNew(Expr exprSrc)
        {
            Debug.Assert(exprSrc != null);

            NullableType pNubSourceType = TypeManager.GetNullable(exprSrc.Type);
            AggregateType pSourceType = pNubSourceType.GetAts();
            MethodSymbol meth = PredefinedMembers.GetMethod(PREDEFMETH.PM_G_OPTIONAL_CTOR);
            MethWithInst methwithinst = new MethWithInst(meth, pSourceType, TypeArray.Empty);
            ExprMemberGroup memgroup = ExprFactory.CreateMemGroup(null, methwithinst);
            return ExprFactory.CreateCall(EXPRFLAG.EXF_NEWOBJCALL | EXPRFLAG.EXF_CANTBENULL, pNubSourceType, exprSrc, memgroup, methwithinst);
        }
    }
}
