using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Reflection; using System.Text; using InnerSpaceAPI; using LavishScriptAPI; using LavishScriptAPI.Interfaces; using MRBot.IsxEq2.Extensions; namespace MRBot.IsxEq2.Helpers { /// /// CallbackDelegate used in trace logging. /// /// /// public delegate void CallbackDelegate(string method, string args); /// /// Class used for trace logging. /// public class Tracing { /// /// Callback to use for logging. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2211:NonConstantFieldsShouldNotBeVisible")] public static CallbackDelegate Callback; /// /// Add a callback delegate for trace logging. /// /// public static void AddCallback(CallbackDelegate callbackDelegate) { Callback = callbackDelegate; } /// /// Clear the callback delegate. /// public static void RemoveCallback() { Callback = null; } /// /// Use the callback delegate to send a log message. /// /// /// public static void SendCallback(string message, params object[] args) { if (Callback == null) return; var argString = new StringBuilder(); if (args.Length > 0) { argString.Append(args[0].ToString()); } for (int idx = 1; idx < args.Length; idx++) { argString.Append(String.Format(CultureInfo.InvariantCulture, ", {0}", args[idx])); } Callback(message, argString.ToString()); } } public static class Util { private static readonly Dictionary _implementingTypesByInterfaceType = new Dictionary(); private static readonly Dictionary _constructorInfoByType = new Dictionary(); private static T[] PrefixArray(T first, T[] rest) { var newArray = new T[rest.Length + 1]; newArray[0] = first; for (var i = 0; i < rest.Length; i++) newArray[i + 1] = rest[i]; return newArray; } private static Type GetImplementingTypeForInterfaceType(Type interfaceType) { if (!_implementingTypesByInterfaceType.ContainsKey(interfaceType)) { var implementingType = interfaceType.Assembly.GetTypes() .Where(t => t.IsClass) .FirstOrDefault(interfaceType.IsAssignableFrom); if (implementingType == null) { throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Could not find implementing type for interface type {0}.", interfaceType.Name)); } _implementingTypesByInterfaceType.Add(interfaceType, implementingType); } return _implementingTypesByInterfaceType[interfaceType]; } private static ConstructorInfo GetConstructorInfoForType(Type type) { if (!_constructorInfoByType.ContainsKey(type)) { ConstructorInfo constructorInfo; if (type.IsInterface) { var implementingType = GetImplementingTypeForInterfaceType(type); constructorInfo = implementingType.GetConstructor(new[] { typeof(LavishScriptObject) }); } else { constructorInfo = type.GetConstructor(new[] { typeof(LavishScriptObject) }); } if (constructorInfo == null) throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Could not find a constructor for type {0}.", type.Name)); _constructorInfoByType.Add(type, constructorInfo); } return _constructorInfoByType[type]; } [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1820:TestForEmptyStringsUsingStringLength")] private static List IndexToLavishScriptObjectList(LavishScriptObject index, string lsTypeName) { var list = new List(); var count = index.GetMember("Used"); if (count == 0) { InnerSpace.Echo($"IndexToLavishScriptObjectList: count is 0"); return list; } //Tracing.SendCallback(methodName, "get constructor info"); var constructorInfo = GetConstructorInfoForType(typeof(T)); //Tracing.SendCallback(methodName, "loop add items"); for (var i = 1; i <= count; i++) { var objectLso = index.GetIndex(i.ToString(CultureInfo.CurrentCulture)); if (LavishScriptObject.IsNullOrInvalid(objectLso)) { Tracing.SendCallback(String.Format(CultureInfo.InvariantCulture, "Error: Index contains invalid LSO. NewObject will fail; aborting.")); InnerSpace.Echo($"Error: Index contains invalid LSO at index {i}. NewObject will fail; aborting."); continue; } var objectId = objectLso.GetString("ID"); if (objectId == null) { Tracing.SendCallback(String.Format(CultureInfo.InvariantCulture, "Error: LStype \"{0}\" has no ID member. NewObject will fail; aborting.", lsTypeName)); InnerSpace.Echo($"Error: LStype {lsTypeName} has no ID member. NewObject will fail; abortig."); return list; } if (objectId == string.Empty) { Tracing.SendCallback(String.Format(CultureInfo.InvariantCulture, "Error: LStype \"{0}\" has an ID member but it is returning an empty string. NewObject will fail; aborting.", lsTypeName)); InnerSpace.Echo($"Error: LStype {lsTypeName} has an ID member but it is returning an empty string. NewObject will fail; aborting."); return list; } var lsObject = LavishScript.Objects.NewObject(lsTypeName, objectId); var item = (T)constructorInfo.Invoke(new object[] { lsObject }); list.Add(item); } return list; } private static List IndexToStructList(LavishScriptObject index) { InnerSpace.Echo($"IndexToStructList: {typeof(T).Name}"); var list = new List(); var count = index.GetMember("Used"); for (var i = 1; i <= count; i++) list.Add(index.GetIndex(i.ToString(CultureInfo.CurrentCulture))); return list; } public static List IndexToList(LavishScriptObject index, string lsTypeName) { InnerSpace.Echo($"IndexToList: {typeof(T).Name}"); return (typeof(LavishScriptObject)).IsAssignableFrom(typeof(T)) ? IndexToLavishScriptObjectList(index, lsTypeName) : IndexToStructList(index); } public static T GetIndexMember(LavishScriptObject index, int number) { if (typeof(T).IsSubclassOf(typeof(LavishScriptObject))) return (T)typeof(T).GetConstructor(new[] { typeof(LavishScriptObject) }).Invoke(new object[] { index.GetIndex(number.ToString(CultureInfo.CurrentCulture)) }); return index.GetIndex(number.ToString(CultureInfo.CurrentCulture)); } /// /// Translate an index of a given LavishScript type returned from the given method on the given object to a List of a .NET datatype equivalent. /// /// /// /// /// /// /// public static List GetListFromMethod(this ILSObject obj, string methodName, string lsTypeName, params string[] args) { if (obj == null || !obj.IsValid) { InnerSpace.Echo("GetListFromMethod: obj is null or invalid"); return null; } using (var index = LavishScript.Objects.NewObject("index:" + lsTypeName)) { string[] allargs; if (args.Length > 0) { allargs = PrefixArray(index.LSReference, args); } else { allargs = new string[1]; allargs[0] = index.LSReference; } if (!obj.ExecuteMethod(methodName, allargs)) { InnerSpace.Echo($"Failed to execute method {methodName}"); return null; } using (var used = index.GetMember("Used")) { if (LavishScriptObject.IsNullOrInvalid(used)) { InnerSpace.Echo($"LSO.IsNullOrInvalid (used)"); return null; } InnerSpace.Echo($"used: {used.GetValue()}"); } var list = IndexToList(index, lsTypeName); return list; } } internal static T GetFromIndexMethod(ILSObject obj, string methodName, string lsTypeName, int number, params string[] args) { // argument is 0-based number += 1; if (obj == null || !obj.IsValid || number <= 0) return default; using (var index = LavishScript.Objects.NewObject("index:" + lsTypeName)) { var allargs = PrefixArray(index.LSReference, args); if (!obj.ExecuteMethod(methodName, allargs)) return default; using (var used = index.GetMember("Used")) { // if it failed or we want one off the end, return if (LavishScriptObject.IsNullOrInvalid(used) || used.GetValue() < number) return default; } var member = GetIndexMember(index, number); return member; } } /// /// Translate an index of a given LavishScript type returned from the given member on the given object to a List of a .NET datatype equivalent. /// /// /// /// /// /// /// public static List GetListFromMember(this LavishScriptObject obj, string memberName, string lsTypeName, params string[] args) { if (obj == null || !obj.IsValid) return null; //Tracing.SendCallback(methodName, "new index"); using (var index = LavishScript.Objects.NewObject("index:" + lsTypeName)) { //Tracing.SendCallback(methodName, "arg condensing"); var allargs = PrefixArray(index.LSReference, args); //Tracing.SendCallback(methodName, "getmember retval"); using (var retval = obj.GetMember(memberName, allargs)) { if (LavishScriptObject.IsNullOrInvalid(retval)) return null; } //Tracing.SendCallback(methodName, "index to list"); var list = IndexToList(index, lsTypeName); //Tracing.SendCallback(methodName, "invalidate"); //Tracing.SendCallback(methodName, "return"); return list; } } internal static T GetFromIndexMember(ILSObject obj, string memberName, string lsTypeName, int number, params string[] args) { // argument is 0-based number += 1; if (obj == null || !obj.IsValid) return default; using (var index = LavishScript.Objects.NewObject("index:" + lsTypeName)) { var allargs = PrefixArray(index.LSReference, args); using (var retval = obj.GetMember(memberName, allargs)) { if (LavishScriptObject.IsNullOrInvalid(retval) || retval.GetValue() < number) return default; } var member = GetIndexMember(index, number); return member; } } } }