/*
 * Copyright (c) Contributors, http://opensimulator.org/
 * See CONTRIBUTORS.TXT for a full list of copyright holders.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the OpenSimulator Project nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

using System;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;
using System.Text;

using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat;
using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger;
using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list;
using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion;
using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3;

/**
 * @brief Generate code for the backend API calls.
 */
namespace OpenSim.Region.ScriptEngine.Yengine
{
    public abstract class TokenDeclInline: TokenDeclVar
    {
        public static VarDict inlineFunctions = CreateDictionary();

        public abstract void CodeGen(ScriptCodeGen scg, Token errorAt, CompValuTemp result, CompValu[] args);

        private static HashSet<string> noCheckRuns;
        private static string[] keyReturns;

        protected bool isTaggedCallsCheckRun;

        /**
         * @brief Create a dictionary of inline backend API functions.
         */
        private static VarDict CreateDictionary()
        {
            /*
             * For those listed in noCheckRun, we just generate the call (simple computations).
             * For all others, we generate the call then a call to CheckRun().
             * note to self: a change here implies change on magic numbers to invalidate older code and 
             * script state
             */
            noCheckRuns = new HashSet<string>() {
                "llBase64ToString",
                "llCSV2List",
                "llDeleteSubList",
                "llDeleteSubString",
                "llDumpList2String",
                "llEscapeURL",
                "llEuler2Rot",
                "llGetListEntryType",
                "llGetListLength",
                "llGetSubString",
                "llGetUnixTime",
                "llInsertString",
                "llList2CSV",
                "llList2Float",
                "llList2Integer",
                "llList2Key",
                "llList2Rot",
                "llList2String",
                "llList2Vector",
                "llListFindList",
                "llListInsertList",
                "llListRandomize",
                "llListReplaceList",
                "llListSort",
                "llListStatistics",
                "llMD5String",
                "llParseString2List",
                "llParseStringKeepNulls",
                "llRot2Euler",
                "llStringLength",
                "llStringToBase64",
                "llStringTrim",
                "llSubStringIndex",
                "llUnescapeURL",
                "osGetSitActiveRange",
                "osIsNotValidNumber",
                "osSlerp",
                "osApproxEquals",
                "osVecDistSquare",
                "osVecMagSquare",
                "osRound",
                "osGetLinkNumber",
                "osMax",
                "osMin",
                "osIsUUID"
            };

            /*
             * These functions really return a 'key' even though we see them as
             * returning 'string' because OpenSim has key and string as same type.
             */
            keyReturns = new string[] {
                "llAvatarOnLinkSitTarget",
                "llAvatarOnSitTarget",
                "llDetectedKey",
                "llDetectedOwner",
                "llGenerateKey",
                "llGetCreator",
                "llGetInventoryCreator",
                "llGetInventoryKey",
                "llGetKey",
                "llGetLandOwnerAt",
                "llGetLinkKey",
                "llGetNotecardLine",
                "llGetNumberOfNotecardLines",
                "llGetOwner",
                "llGetOwnerKey",
                "llGetPermissionsKey",
                "llHTTPRequest",
                "llList2Key",
                "llRequestAgentData",
                "llRequestDisplayName",
                "llRequestInventoryData",
                "llRequestSecureURL",
                "llRequestSimulatorData",
                "llRequestURL",
                "llRequestUsername",
                "llSendRemoteData",
                "llTransferLindenDollars"
            };

            VarDict ifd = new VarDict(false);
            Type[] oneDoub = new Type[] { typeof(double) };
            Type[] twoDoubs = new Type[] { typeof(double), typeof(double) };
 
            /*
             * Something weird about the code generation for these calls, so they all have their own handwritten code generators.
             */
            new TokenDeclInline_GetFreeMemory(ifd);
            new TokenDeclInline_GetUsedMemory(ifd);

            /*
             * These are all the xmr...() calls directly in XMRInstAbstract.
             * Includes the calls from ScriptBaseClass that has all the stubs
             * which convert XMRInstAbstract to the various <NAME>_Api contexts.
             */
            MethodInfo[] absmeths = typeof(XMRInstAbstract).GetMethods();
            AddInterfaceMethods(ifd, absmeths, null);

            return ifd;
        }

        /**
         * @brief Add API functions from the given interface to list of built-in functions.
         *        Only functions beginning with a lower-case letter are entered, all others ignored.
         * @param ifd = internal function dictionary to add them to
         * @param ifaceMethods = list of API functions
         * @param acf = which field in XMRInstanceSuperType holds method's 'this' pointer
         */
        // this one accepts only names beginning with a lower-case letter
        public static void AddInterfaceMethods(VarDict ifd, MethodInfo[] ifaceMethods, FieldInfo acf)
        {
            List<MethodInfo> lcms = new List<MethodInfo>(ifaceMethods.Length);
            foreach(MethodInfo meth in ifaceMethods)
            {
                string name = meth.Name;
                if((name[0] >= 'a') && (name[0] <= 'z'))
                {
                    lcms.Add(meth);
                }
            }
            AddInterfaceMethods(ifd, lcms.GetEnumerator(), acf);
        }

        // this one accepts all methods given to it
        public static void AddInterfaceMethods(VarDict ifd, IEnumerator<MethodInfo> ifaceMethods, FieldInfo acf)
        {
            ifd ??= inlineFunctions;

            for(ifaceMethods.Reset(); ifaceMethods.MoveNext();)
            {
                MethodInfo ifaceMethod = ifaceMethods.Current;
                try
                {
                    /*
                     * See if we will generate a call to CheckRun() right 
                     * after we generate a call to the function.
                     * If function begins with xmr, assume we will not call CheckRun()
                     * Otherwise, assume we will call CheckRun()
                     */
                    bool dcr = !(ifaceMethod.Name.StartsWith("xmr") || noCheckRuns.Contains(ifaceMethod.Name));

                    /*
                     * Add function to dictionary.
                     */
                    new TokenDeclInline_BEApi(ifd, dcr, ifaceMethod, acf);
                }
                catch
                {
                    ///??? IGNORE ANY THAT FAIL - LIKE UNRECOGNIZED TYPE ???///
                    ///???                          and OVERLOADED NAMES ???///
                }
            }
        }

        /**
         * @brief Add an inline function definition to the dictionary.
         * @param ifd        = dictionary to add inline definition to
         * @param doCheckRun = true iff the generated code or the function itself can possibly call CheckRun()
         * @param nameArgSig = inline function signature string, in form <name>(<arglsltypes>,...)
         * @param retType    = return type, use TokenTypeVoid if no return value
         */
        protected TokenDeclInline(VarDict ifd,
                                   bool doCheckRun,
                                   string nameArgSig,
                                   TokenType retType)
                : base(null, null, null)
        {
            this.retType = retType;
            this.triviality = doCheckRun ? Triviality.complex : Triviality.trivial;

            int j = nameArgSig.IndexOf('(');
            this.name = new TokenName(null, nameArgSig.Substring(0, j++));

            this.argDecl = new TokenArgDecl(null);
            if(nameArgSig[j] != ')')
            {
                int i;
                TokenName name;
                TokenType type;

                for(i = j; nameArgSig[i] != ')'; i++)
                {
                    if(nameArgSig[i] == ',')
                    {
                        type = TokenType.FromLSLType(null, nameArgSig.Substring(j, i - j));
                        name = new TokenName(null, "arg" + this.argDecl.varDict.Count);
                        this.argDecl.AddArg(type, name);
                        j = i + 1;
                    }
                }

                type = TokenType.FromLSLType(null, nameArgSig.Substring(j, i - j));
                name = new TokenName(null, "arg" + this.argDecl.varDict.Count);
                this.argDecl.AddArg(type, name);
            }

            this.location = new CompValuInline(this);
            if(ifd == null)
                ifd = inlineFunctions;
            ifd.AddEntry(this);
        }

        protected TokenDeclInline(VarDict ifd,
                                   bool doCheckRun,
                                   MethodInfo methInfo)
                : base(null, null, null)
        {
            isTaggedCallsCheckRun = IsTaggedCallsCheckRun(methInfo);
            name = new TokenName(null, methInfo.Name);
            retType = GetRetType(methInfo, TokenType.FromSysType(null, methInfo.ReturnType));
            argDecl = GetArgDecl(methInfo.GetParameters());
            triviality = (doCheckRun || isTaggedCallsCheckRun) ? Triviality.complex : Triviality.trivial;
            location = new CompValuInline(this);

            ifd ??= inlineFunctions;
            ifd.AddEntry(this);
        }

        private static TokenArgDecl GetArgDecl(ParameterInfo[] parameters)
        {
            TokenArgDecl argDecl = new TokenArgDecl(null);
            foreach(ParameterInfo pi in parameters)
            {
                TokenType type = TokenType.FromSysType(null, pi.ParameterType);
                TokenName name = new TokenName(null, pi.Name);
                argDecl.AddArg(type, name);
            }
            return argDecl;
        }

        /**
         * @brief The above code assumes all methods beginning with 'xmr' are trivial, ie, 
         *        they do not call CheckRun() and also we do not generate a CheckRun() 
         *        call after they return.  So if an 'xmr' method does call CheckRun(), it 
         *        must be tagged with attribute 'xmrMethodCallsCheckRunAttribute' so we know 
         *        the method is not trivial.  But in neither case do we emit our own call 
         *        to CheckRun(), the 'xmr' method must do its own.  We do however set up a
         *        call label before the call to the non-trivial 'xmr' method so when we are
         *        restoring the call stack, the restore will call directly in to the 'xmr'
         *        method without re-executing any code before the call to the 'xmr' method.
         */
        private static bool IsTaggedCallsCheckRun(MethodInfo methInfo)
        {
            return (methInfo != null) &&
                Attribute.IsDefined(methInfo, typeof(xmrMethodCallsCheckRunAttribute));
        }

        /**
         * @brief The dumbass OpenSim has key and string as the same type so non-ll
         *        methods must be tagged with xmrMethodReturnsKeyAttribute if we
         *        are to think they return a key type, otherwise we will think they
         *        return string.
         */
        private static TokenType GetRetType(MethodInfo methInfo, TokenType retType)
        {
            if((methInfo != null) && (retType != null) && (retType is TokenTypeStr))
            {
                if(Attribute.IsDefined(methInfo, typeof(xmrMethodReturnsKeyAttribute)))
                {
                    return ChangeToKeyType(retType);
                }

                string mn = methInfo.Name;
                foreach(string kr in keyReturns)
                {
                    if(kr == mn)
                        return ChangeToKeyType(retType);
                }

            }
            return retType;
        }
        private static TokenType ChangeToKeyType(TokenType retType)
        {
            if(retType is TokenTypeLSLString)
            {
                retType = new TokenTypeLSLKey(null);
            }
            else
            {
                retType = new TokenTypeKey(null);
            }
            return retType;
        }

        public virtual MethodInfo GetMethodInfo()
        {
            return null;
        }

        /**
         * @brief Print out a list of all the built-in functions and constants.
         */
        public delegate void WriteLine(string str);
        public static void PrintBuiltins(bool inclNoisyTag, WriteLine writeLine)
        {
            writeLine("\nBuilt-in functions:\n");
            SortedDictionary<string, TokenDeclInline> bifs = new SortedDictionary<string, TokenDeclInline>();
            foreach(TokenDeclVar bif in TokenDeclInline.inlineFunctions)
            {
                bifs.Add(bif.fullName, (TokenDeclInline)bif);
            }
            foreach(TokenDeclInline bif in bifs.Values)
            {
                char noisy = (!inclNoisyTag || !IsTaggedNoisy(bif.GetMethodInfo())) ? ' ' : (bif.retType is TokenTypeVoid) ? 'N' : 'R';
                writeLine(noisy + "   " + bif.retType.ToString().PadLeft(8) + " " + bif.fullName);
            }
            if(inclNoisyTag)
            {
                writeLine("\nN - stub that writes name and arguments to stdout");
                writeLine("R - stub that writes name and arguments to stdout then reads return value from stdin");
                writeLine("    format is:  function_name : return_value");
                writeLine("      example:  llKey2Name:\"Kunta Kinte\"");
            }

            writeLine("\nBuilt-in constants:\n");
            SortedDictionary<string, ScriptConst> scs = new SortedDictionary<string, ScriptConst>();
            int widest = 0;
            foreach(ScriptConst sc in ScriptConst.scriptConstants.Values)
            {
                if(widest < sc.name.Length)
                    widest = sc.name.Length;
                scs.Add(sc.name, sc);
            }
            foreach(ScriptConst sc in scs.Values)
            {
                writeLine("    " + sc.rVal.type.ToString().PadLeft(8) + " " + sc.name.PadRight(widest) + " = " + BuiltInConstVal(sc.rVal));
            }
        }

        public static bool IsTaggedNoisy(MethodInfo methInfo)
        {
            return (methInfo != null) && Attribute.IsDefined(methInfo, typeof(xmrMethodIsNoisyAttribute));
        }

        public static string BuiltInConstVal(CompValu rVal)
        {
            if(rVal is CompValuInteger)
            {
                int x = ((CompValuInteger)rVal).x;
                return "0x" + x.ToString("X8") + " = " + x.ToString().PadLeft(11);
            }
            if(rVal is CompValuFloat)
                return ((CompValuFloat)rVal).x.ToString();
            if(rVal is CompValuString)
            {
                StringBuilder sb = new StringBuilder();
                PrintParam(sb, ((CompValuString)rVal).x);
                return sb.ToString();
            }
            if(rVal is CompValuSField)
            {
                FieldInfo fi = ((CompValuSField)rVal).field;
                StringBuilder sb = new StringBuilder();
                PrintParam(sb, fi.GetValue(null));
                return sb.ToString();
            }
            return rVal.ToString();  // just prints the type
        }

        public static void PrintParam(StringBuilder sb, object p)
        {
            if(p == null)
            {
                sb.Append("null");
            }
            else if(p is LSL_List)
            {
                sb.Append('[');
                object[] d = ((LSL_List)p).Data;
                for(int i = 0; i < d.Length; i++)
                {
                    if(i > 0)
                        sb.Append(',');
                    PrintParam(sb, d[i]);
                }
                sb.Append(']');
            }
            else if(p is LSL_Rotation)
            {
                LSL_Rotation r = (LSL_Rotation)p;
                sb.Append('<');
                sb.Append(r.x);
                sb.Append(',');
                sb.Append(r.y);
                sb.Append(',');
                sb.Append(r.z);
                sb.Append(',');
                sb.Append(r.s);
                sb.Append('>');
            }
            else if(p is LSL_String)
            {
                PrintParamString(sb, (string)(LSL_String)p);
            }
            else if(p is LSL_Vector)
            {
                LSL_Vector v = (LSL_Vector)p;
                sb.Append('<');
                sb.Append(v.x);
                sb.Append(',');
                sb.Append(v.y);
                sb.Append(',');
                sb.Append(v.z);
                sb.Append('>');
            }
            else if(p is string)
            {
                PrintParamString(sb, (string)p);
            }
            else
            {
                sb.Append(p.ToString());
            }
        }

        public static void PrintParamString(StringBuilder sb, string p)
        {
            sb.Append('"');
            foreach(char c in p)
            {
                if(c == '\b')
                {
                    sb.Append("\\b");
                    continue;
                }
                if(c == '\n')
                {
                    sb.Append("\\n");
                    continue;
                }
                if(c == '\r')
                {
                    sb.Append("\\r");
                    continue;
                }
                if(c == '\t')
                {
                    sb.Append("\\t");
                    continue;
                }
                if(c == '"')
                {
                    sb.Append("\\\"");
                    continue;
                }
                if(c == '\\')
                {
                    sb.Append("\\\\");
                    continue;
                }
                sb.Append(c);
            }
            sb.Append('"');
        }
    }

    /**
     * @brief Code generators...
     * @param scg = script we are generating code for
     * @param result = type/location for result (type matches function definition)
     * @param args = type/location of arguments (types match function definition)
     */

    /*
    public class TokenDeclInline_LLAbs: TokenDeclInline
    {
        public TokenDeclInline_LLAbs(VarDict ifd)
                : base(ifd, false, "llAbs(integer)", new TokenTypeInt(null)) { }

        public override void CodeGen(ScriptCodeGen scg, Token errorAt, CompValuTemp result, CompValu[] args)
        {
            ScriptMyLabel itsPosLabel = scg.ilGen.DefineLabel("llAbstemp");

            args[0].PushVal(scg, errorAt);
            scg.ilGen.Emit(errorAt, OpCodes.Dup);
            scg.ilGen.Emit(errorAt, OpCodes.Ldc_I4_0);
            scg.ilGen.Emit(errorAt, OpCodes.Bge_S, itsPosLabel);
            scg.ilGen.Emit(errorAt, OpCodes.Neg);
            scg.ilGen.MarkLabel(itsPosLabel);
            result.Pop(scg, errorAt, retType);
        }
    }

    public class TokenDeclInline_Math: TokenDeclInline
    {
        private MethodInfo methInfo;

        public TokenDeclInline_Math(VarDict ifd, string sig, string name, Type[] args)
                : base(ifd, false, sig, new TokenTypeFloat(null))
        {
            methInfo = ScriptCodeGen.GetStaticMethod(typeof(System.Math), name, args);
        }

        public override void CodeGen(ScriptCodeGen scg, Token errorAt, CompValuTemp result, CompValu[] args)
        {
            for(int i = 0; i < args.Length; i++)
            {
                args[i].PushVal(scg, errorAt, argDecl.types[i]);
            }
            scg.ilGen.Emit(errorAt, OpCodes.Call, methInfo);
            result.Pop(scg, errorAt, retType);
        }
    }

    public class TokenDeclInline_LLRound: TokenDeclInline
    {

        private static MethodInfo roundMethInfo = ScriptCodeGen.GetStaticMethod(typeof(System.Math), "Round",
                new Type[] { typeof(double), typeof(MidpointRounding) });

        public TokenDeclInline_LLRound(VarDict ifd)
                : base(ifd, false, "llRound(float)", new TokenTypeInt(null)) { }

        public override void CodeGen(ScriptCodeGen scg, Token errorAt, CompValuTemp result, CompValu[] args)
        {
            args[0].PushVal(scg, errorAt, new TokenTypeFloat(null));
            scg.ilGen.Emit(errorAt, OpCodes.Ldc_I4, (int)System.MidpointRounding.AwayFromZero);
            scg.ilGen.Emit(errorAt, OpCodes.Call, roundMethInfo);
            result.Pop(scg, errorAt, new TokenTypeFloat(null));
        }
    }
    */
    public class TokenDeclInline_GetFreeMemory: TokenDeclInline
    {
        private static readonly MethodInfo getFreeMemMethInfo = typeof(XMRInstAbstract).GetMethod("xmrHeapLeft", new Type[] { });

        public TokenDeclInline_GetFreeMemory(VarDict ifd)
                : base(ifd, false, "llGetFreeMemory()", new TokenTypeInt(null)) { }

        // appears as llGetFreeMemory() in script source code
        // but actually calls xmrHeapLeft()
        public override void CodeGen(ScriptCodeGen scg, Token errorAt, CompValuTemp result, CompValu[] args)
        {
            scg.PushXMRInst();
            scg.ilGen.Emit(errorAt, OpCodes.Call, getFreeMemMethInfo);
            result.Pop(scg, errorAt, new TokenTypeInt(null));
        }
    }

    public class TokenDeclInline_GetUsedMemory: TokenDeclInline
    {
        private static readonly MethodInfo getUsedMemMethInfo = typeof(XMRInstAbstract).GetMethod("xmrHeapUsed", new Type[] { });

        public TokenDeclInline_GetUsedMemory(VarDict ifd)
                : base(ifd, false, "llGetUsedMemory()", new TokenTypeInt(null)) { }

        // appears as llGetUsedMemory() in script source code
        // but actually calls xmrHeapUsed()
        public override void CodeGen(ScriptCodeGen scg, Token errorAt, CompValuTemp result, CompValu[] args)
        {
            scg.PushXMRInst();
            scg.ilGen.Emit(errorAt, OpCodes.Call, getUsedMemMethInfo);
            result.Pop(scg, errorAt, new TokenTypeInt(null));
        }
    }
 

    /**
     * @brief Generate code for the usual ll...() functions.
     */
    public class TokenDeclInline_BEApi: TokenDeclInline
    {
        //        private static readonly MethodInfo fixLLParcelMediaQuery = ScriptCodeGen.GetStaticMethod 
        //                (typeof (XMRInstAbstract), "FixLLParcelMediaQuery", new Type[] { typeof (LSL_List) });

        //        private static readonly MethodInfo fixLLParcelMediaCommandList = ScriptCodeGen.GetStaticMethod 
        //                (typeof (XMRInstAbstract), "FixLLParcelMediaCommandList", new Type[] { typeof (LSL_List) });

        public bool doCheckRun;
        private FieldInfo apiContextField;
        private MethodInfo methInfo;

        /**
         * @brief Constructor
         * @param ifd = dictionary to add the function to
         * @param dcr = append a call to CheckRun()
         * @param methInfo = ll...() method to be called
         */
        public TokenDeclInline_BEApi(VarDict ifd, bool dcr, MethodInfo methInfo, FieldInfo acf)
                : base(ifd, dcr, methInfo)
        {
            this.methInfo = methInfo;
            doCheckRun = dcr;
            apiContextField = acf;
        }

        public override MethodInfo GetMethodInfo()
        {
            return methInfo;
        }

        /**
         * @brief Generate call to backend API function (eg llSay()) maybe followed by a call to CheckRun().
         * @param scg    = script being compiled
         * @param result = where to place result (might be void)
         * @param args   = script-visible arguments to pass to API function
         */
        public override void CodeGen(ScriptCodeGen scg, Token errorAt, CompValuTemp result, CompValu[] args)
        {
            if(isTaggedCallsCheckRun)
            {                                                   // see if 'xmr' method that calls CheckRun() internally
                new ScriptCodeGen.CallLabel(scg, errorAt);     // if so, put a call label immediately before it
                                                               // .. so restoring the frame will jump immediately to the
                                                               // .. call without re-executing any code before this
            }
            if(!methInfo.IsStatic)
            {
                scg.PushXMRInst();                          // XMRInstanceSuperType pointer
                if(apiContextField != null)                 // 'this' pointer for API function
                    scg.ilGen.Emit(errorAt, OpCodes.Ldfld, apiContextField);

            }
            for(int i = 0; i < args.Length; i++)             // push arguments, boxing/unboxing as needed
                args[i].PushVal(scg, errorAt, argDecl.types[i]);

            // this should not be needed
            //            if (methInfo.Name == "llParcelMediaQuery") {
            //                scg.ilGen.Emit (errorAt, OpCodes.Call, fixLLParcelMediaQuery);
            //            }
            // this should not be needed
            //            if (methInfo.Name == "llParcelMediaCommandList") {
            //                scg.ilGen.Emit (errorAt, OpCodes.Call, fixLLParcelMediaCommandList);
            //            }
            if(methInfo.IsVirtual)                            // call API function
                scg.ilGen.Emit(errorAt, OpCodes.Callvirt, methInfo);
            else
                scg.ilGen.Emit(errorAt, OpCodes.Call, methInfo);

            result.Pop(scg, errorAt, retType);                  // pop result, boxing/unboxing as needed
            if(isTaggedCallsCheckRun)
                scg.openCallLabel = null;

            if(doCheckRun)
                scg.EmitCallCheckRun(errorAt, false);       // maybe call CheckRun()
        }
    }
}
