using Edge.Core.Parser.BinaryParser;
using Edge.Core.Processor.Communicator;
using Edge.Core.Processor.Dispatcher.Attributes;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.Json.Serialization;
using System.Text.Json;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;


namespace Edge.Core.Processor.Dispatcher
{

    public static class ObjectInstanceCreator
    {
        public static ILogger mainLogger =  Microsoft.Extensions.Logging.Abstractions.NullLogger.Instance;
        static JsonSerializerOptions jsonSerializerOptions;
        static string plugInFolder = "plugin";
        private static IEnumerable<Assembly> LoadPlugInAssemblies()
        {
            //var assemblyFileInfos = new DirectoryInfo(Directory.GetCurrentDirectory()).GetFiles()
            //        .Where(f => f.Extension != null
            //           && (f.Extension.ToLower() == ".dll" || f.Extension.ToLower() == ".exe")
            //           && !f.Name.Contains("Microsoft"));
            //if (mainLogger.IsEnabled(LogLevel.Trace))
            //{
            //    mainLogger.LogTrace("LoadPlugInAssemblies, root folder(" + Directory.GetCurrentDirectory() + ") loaded assembly files: " +
            //        assemblyFileInfos.Select(fi => fi.Name + ", Length: " + fi.Length +
            //            ", CreationTimeUtc: " + fi.CreationTimeUtc +
            //            ", LastAccessTimeUtc: " + fi.LastAccessTimeUtc + ", LastWriteTimeUtc: " + fi.LastWriteTimeUtc)
            //        .Aggregate(Environment.NewLine, (acc, n) => acc + n + Environment.NewLine));
            //}

            if (Directory.Exists(Directory.GetCurrentDirectory() + "\\" + plugInFolder))
            {
                var plugInFolderAssemblyFileInfos = new DirectoryInfo(Directory.GetCurrentDirectory() + "\\" + plugInFolder).GetFiles()
                      .Where(f => f.Extension != null
                         && (f.Extension.ToLower() == ".dll" || f.Extension.ToLower() == ".exe")
                         && !f.Name.Contains("Microsoft"));
                if (mainLogger.IsEnabled(LogLevel.Trace))
                {
                    mainLogger.LogTrace("LoadPlugInAssemblies, plugIn folder(path: " + plugInFolder + ") contains assembly files: " +
                        plugInFolderAssemblyFileInfos.Select(fi => fi.Name + ", Length: " + fi.Length +
                        ", CreationTimeUtc: " + fi.CreationTimeUtc +
                        ", LastAccessTimeUtc: " + fi.LastAccessTimeUtc + ", LastWriteTimeUtc: " + fi.LastWriteTimeUtc)
                    .Aggregate(Environment.NewLine, (acc, n) => acc + n + Environment.NewLine));
                }

                var rootFolderAssemblyFileInfos = new DirectoryInfo(Directory.GetCurrentDirectory()).GetFiles()
                        .Where(f => f.Extension != null
                           && (f.Extension.ToLower() == ".dll" || f.Extension.ToLower() == ".exe")
                           && !f.Name.Contains("Microsoft"));
                var plugInFolderDuplicatedFileInfos =
                    plugInFolderAssemblyFileInfos.Join(rootFolderAssemblyFileInfos, fi => fi.Name, fi => fi.Name, (outer, inner) => outer);

                if (mainLogger.IsEnabled(LogLevel.Information))
                {
                    if (plugInFolderDuplicatedFileInfos.Any())
                        mainLogger.LogInformation("LoadPlugInAssemblies, duplicated PlugIn assembly files found in plugIn folder(path: " + plugInFolder + "): " +
                            plugInFolderDuplicatedFileInfos.Select(fi => fi.Name + ", Length: " + fi.Length +
                                ", CreationTimeUtc: " + fi.CreationTimeUtc +
                                ", LastAccessTimeUtc: " + fi.LastAccessTimeUtc +
                                ", LastWriteTimeUtc: " + fi.LastWriteTimeUtc)
                            .Aggregate(Environment.NewLine, (acc, n) => acc + n + Environment.NewLine) +
                            Environment.NewLine + "Will keep the one in rootFolder which should already loaded into CurrentDomain");
                }

                return plugInFolderAssemblyFileInfos.Where(fi => !plugInFolderDuplicatedFileInfos.Select(dFi => dFi.Name).Contains(fi.Name))
                    .Select(fi =>
                    {
                        try
                        {
                            var ass = Assembly.LoadFrom(fi.FullName);
                            if (mainLogger.IsEnabled(LogLevel.Trace))
                            {
                                mainLogger.LogTrace("LoadPlugInAssemblies, Loaded assembly by Assembly.LoadFrom: " + fi.FullName);
                            }

                            return ass;
                        }
                        catch (Exception eeee)
                        {
                            mainLogger.LogInformation("LoadPlugInAssemblies, Failed to Loaded assembly by Assembly.LoadFrom: " + fi.FullName +
                                Environment.NewLine + eeee + Environment.NewLine + "Will skip this assembly...");
                            return null;
                        }
                    });
            }
            else
            {
                mainLogger.LogInformation("LoadPlugInAssemblies, failed due to no plugIn folder found");
                return null;
            };
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="assemblyFullName">Gets the full name of the assembly, also known as the display name.</param>
        /// <returns></returns>
        internal static Assembly DefaultSearchLocalAssemblyFileAssemblyResolver(string assemblyFullName, Assembly? requestingAssembly)
        {
            try
            {
                var targetAssembly = CurrentDomainAssemblies.Where(ass => ass.FullName == assemblyFullName).FirstOrDefault();
                if (targetAssembly == null)
                {
                    if (mainLogger.IsEnabled(LogLevel.Information))
                        mainLogger.LogInformation("AssemblyResolver, could not resolving: " + assemblyFullName +
                            " from CurrentDomain assemblies" +
                            ", the requestingAssembly is: " + (requestingAssembly?.FullName ?? "") +
                            ", Try loading PlugIn...");

                    var plugInAssemblies = LoadPlugInAssemblies();
                    if (plugInAssemblies != null)
                        foreach (var piAss in plugInAssemblies)
                        {
                            if (piAss != null && piAss.FullName == assemblyFullName) { targetAssembly = piAss; break; }
                        }
                }

                if (mainLogger.IsEnabled(LogLevel.Information))
                    if (targetAssembly != null)
                        mainLogger.LogInformation("AssemblyResolver, resolved: " + assemblyFullName +
                            " as assembly: " + (targetAssembly?.FullName ?? "") +
                            ", the requestingAssembly is: " + (requestingAssembly?.FullName ?? ""));
                    else
                        mainLogger.LogInformation("AssemblyResolver, failed for resolving: " + assemblyFullName +
                            ", the requestingAssembly is: " + (requestingAssembly?.FullName ?? ""));
                return targetAssembly;
            }
            catch (Exception eeee)
            {
                mainLogger.LogInformation("AssemblyResolver, failed for resolving: " + assemblyFullName +
                              " with exception: " + eeee);
                return null;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="typeFullName">Gets the fully qualified name of the type, including its namespace but not its assembly.</param>
        /// <returns></returns>
        internal static Type DefaultSearchLocalAssemblyFileTypeResolver(string typeFullName, Assembly? requestingAssembly)
        {
            var targetType = CurrentDomainAssemblies.Where(ass => !ass.FullName.Contains("Microsoft"))
                .SelectMany(a => { Type[] ts = new Type[] { }; try { ts = a.GetTypes(); } catch { } return ts; }).FirstOrDefault(t => t.FullName == typeFullName);
            if (targetType == null)
            {
                if (mainLogger.IsEnabled(LogLevel.Information))
                    mainLogger.LogInformation("TypeResolver, could not resolving: " + typeFullName + " from CurrentDomain assemblies" +
                        ", the requestingAssembly is: " + (requestingAssembly?.FullName ?? "") +
                        ", Try loading plugIn from folder path: " + plugInFolder);
                var plugInAssemblies = LoadPlugInAssemblies();
                if (plugInAssemblies != null)
                    foreach (var piAss in plugInAssemblies.Where(ass => ass != null))
                    {
                        try
                        {
                            targetType = piAss.GetTypes().FirstOrDefault(t => t.FullName == typeFullName);
                            if (targetType == null) continue;
                            else break;
                        }
                        catch (Exception eeee)
                        { continue; }
                    }
            }

            if (mainLogger.IsEnabled(LogLevel.Information))
                if (targetType != null)
                    mainLogger.LogInformation("TypeResolver, resolved: " + typeFullName + " as type: " + (targetType?.FullName ?? "") +
                        " in assembly: " + (targetType?.Assembly?.FullName ?? "") +
                        ", the requestingAssembly is: " + (requestingAssembly?.FullName ?? ""));
                else
                    mainLogger.LogInformation("TypeResolver, failed for resolving: " + typeFullName + ", the requestingAssembly is: " + (requestingAssembly?.FullName ?? ""));
            return targetType;
        }

        public static IEnumerable<Assembly> CurrentDomainAssemblies { get; private set; }


        static ObjectInstanceCreator()
        {
            var reloadAll_avoid_missing_typesSpecifiedWithoutAssemblyQualifiedName_fi =
                new DirectoryInfo(Directory.GetCurrentDirectory()).GetFiles()
                         .Where(f => f.Extension != null
                            && (f.Extension.ToLower() == ".dll" || f.Extension.ToLower() == ".exe")
                            && !f.Name.Contains("Microsoft"));
            foreach (var fi in reloadAll_avoid_missing_typesSpecifiedWithoutAssemblyQualifiedName_fi)
            {
                try
                {
                    //duplicated ass will be auto ignored.
                    var ass = Assembly.LoadFrom(fi.FullName);
                }
                catch (Exception eeee)
                {
                }
            }

            CurrentDomainAssemblies = AppDomain.CurrentDomain.GetAssemblies();
            AppDomain.CurrentDomain.TypeResolve += (s, args) =>
            {
                var type = ObjectInstanceCreator.DefaultSearchLocalAssemblyFileTypeResolver(args.Name, args.RequestingAssembly);
                return type?.Assembly;
            };
            AppDomain.CurrentDomain.AssemblyResolve += (s, args) =>
            {
                return DefaultSearchLocalAssemblyFileAssemblyResolver(args.Name, args.RequestingAssembly);
            };
            jsonSerializerOptions = new JsonSerializerOptions()
            {
                WriteIndented = true,
                PropertyNameCaseInsensitive = true,
            };
            jsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
        }

        public static object Create(string fullTypeStr, Type[] genericTypeParameters, JsonElement[] ctorInputJsonParameters, object[] dependencies)
        {
            if (string.IsNullOrEmpty(fullTypeStr)) throw new ArgumentNullException(nameof(fullTypeStr));
            var type = Type.GetType(fullTypeStr);
            if (type == null) throw new InvalidOperationException("Could not get the type for fullTypeStr: " + fullTypeStr);
            if (type.IsGenericType)
                if (genericTypeParameters == null || !genericTypeParameters.Any())
                    throw new InvalidOperationException("Must provide Generic type args for instantiate generic type: " + type.FullName);
                else
                    type = type.MakeGenericType(genericTypeParameters);
            var potentialCtors = type.GetConstructors(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
            if ((ctorInputJsonParameters?.Length ?? 0) == 0
                && (potentialCtors == null || !potentialCtors.Any() || potentialCtors.Any(pct => pct.GetParameters().Length == 0)))
                return Activator.CreateInstance(type, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, null, null, null);

            if ((dependencies == null || !dependencies.Any()) && (potentialCtors == null || !potentialCtors.Any())) throw new InvalidOperationException("Can't find constructor with parameter count ==" + (ctorInputJsonParameters?.Length ?? 0) + " for type: " + fullTypeStr);

            List<object> ctrParameterInstances = new List<object>();
            foreach (var ctr in potentialCtors)
            {
                int injectedTimes = 0;
                int nextCtorParameterIndex = 0;
                for (int i = 0; i < ctr.GetParameters().Length; i++)
                {
                    var ctrParaInfo = ctr.GetParameters()[i];
                    try
                    {
                        var part = dependencies?.FirstOrDefault(ip =>
                        {
                            if (ip == null) return false;
                            //if (ctrParaInfo.ParameterType.IsGenericType && ctrParaInfo.ParameterType.ge)
                            //    return IsAssignableToGenericType(ctrParaInfo.ParameterType, ip.GetType());
                            var debug = ctrParaInfo.ParameterType;
                            return debug.IsAssignableFrom(ip.GetType());
                        });
                        if (part != null)
                        { ctrParameterInstances.Add(part); injectedTimes++; continue; }

                        var ctrParameterInstance = System.Text.Json.JsonSerializer.Deserialize(
                            ctorInputJsonParameters[nextCtorParameterIndex].GetRawText(),
                            ctrParaInfo.ParameterType,
                            jsonSerializerOptions);
                        nextCtorParameterIndex++;
                        ctrParameterInstances.Add(ctrParameterInstance);
                    }
                    catch (Exception exx)
                    {
                        /*any exception indicates the operating ctor is not the expecting one, should try next Ctor*/
                        ctrParameterInstances.Clear();
                        break;
                    }
                }

                if (ctrParameterInstances.Any()
                    && ctrParameterInstances.Count == (injectedTimes + ctorInputJsonParameters.Length))
                {
                    try
                    {
                        var instance = Activator.CreateInstance(type, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, ctrParameterInstances.ToArray(), null, null);
                        return instance;
                    }
                    catch (Exception exx)
                    {
                        throw new InvalidOperationException("CreateInstance caught inner exception for type: " + type.FullName + Environment.NewLine + exx);
                    }
                }
                else ctrParameterInstances.Clear();
            }

            throw new JsonConfigNotCompatibleWithAnyCtorDeclarationException(type, null, null, ctorInputJsonParameters);
        }

        /// <summary>
        /// looped all ctors and could not fit anyone with the deserialized types from ctorInputJsonParameters.
        /// </summary>
        public class JsonConfigNotCompatibleWithAnyCtorDeclarationException : Exception
        {
            public JsonConfigNotCompatibleWithAnyCtorDeclarationException(
                Type targetType, ConstructorInfo targetCtor, ParameterInfo targetParameter,
                JsonElement[] ctorInputJsonParameters)
            {
                this.TargetType = targetType;
                this.TargetCtor = targetCtor;
                this.TargetParameter = targetParameter;
                this.CtorInputJsonParameters = ctorInputJsonParameters;
            }

            public Type TargetType { get; private set; }
            public ConstructorInfo TargetCtor { get; private set; }

            public ParameterInfo TargetParameter { get; private set; }

            public JsonElement[] CtorInputJsonParameters { get; private set; }
        }

        //public static T Create<T>(JsonElement[] ctorParameters, Type[] genericTypeParameters, object[] dependencies) //where T : Type
        //{
        //    return (T)Create(typeof(T).FullName, genericTypeParameters, ctorParameters, dependencies);
        //}

        //public static bool IsAssignableToGenericType(Type givenType, Type genericType)
        //{
        //    var interfaceTypes = givenType.GetInterfaces();

        //    foreach (var it in interfaceTypes)
        //    {
        //        if (it.IsGenericType && it.GetGenericTypeDefinition() == genericType)
        //            return true;
        //    }

        //    if (givenType.IsGenericType && givenType.GetGenericTypeDefinition() == genericType)
        //        return true;

        //    Type baseType = givenType.BaseType;
        //    if (baseType == null) return false;

        //    return IsAssignableToGenericType(baseType, genericType);
        //}

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T">must a concrete type, not interface or abstract</typeparam>
        /// <param name="ctorParametersJsonStr"></param>
        /// <param name="genericTypeParameters"></param>
        /// <param name="dependencies"></param>
        /// <returns></returns>
        public static T Create<T>(string ctorParametersJsonStr, Type[] genericTypeParameters, object[] dependencies) //where T : Type
        {
            var inputParameter = JsonDocument.Parse(ctorParametersJsonStr).RootElement;
            JsonElement[] inputParameters = null;
            if (inputParameter.ValueKind == JsonValueKind.Array)
                inputParameters = inputParameter.EnumerateArray().ToArray();
            else if (inputParameter.ValueKind == JsonValueKind.Object)
                inputParameters = new JsonElement[] { inputParameter };
            else if (inputParameter.ValueKind == JsonValueKind.Undefined)
            { }
            else
                inputParameters = new JsonElement[] { inputParameter };
            return (T)Create(typeof(T).FullName, genericTypeParameters, inputParameters, dependencies);
        }

        public static object Create(string fullTypeStr, string ctorParametersJsonStr, Type[] genericTypeParameters, object[] dependencies) //where T : Type
        {
            if (string.IsNullOrEmpty(ctorParametersJsonStr)) ctorParametersJsonStr = "[]";
            var inputParameter = JsonDocument.Parse(ctorParametersJsonStr).RootElement;
            JsonElement[] inputParameters = null;
            if (inputParameter.ValueKind == JsonValueKind.Array)
                inputParameters = inputParameter.EnumerateArray().ToArray();
            else if (inputParameter.ValueKind == JsonValueKind.Object)
                inputParameters = new JsonElement[] { inputParameter };
            else if (inputParameter.ValueKind == JsonValueKind.Undefined)
            { }
            else
                inputParameters = new JsonElement[] { inputParameter };
            return Create(fullTypeStr, genericTypeParameters, inputParameters, dependencies);
        }

        public static T CreateProcessor<T>(ProcessorMetaConfig metaConfig, object[] dependencies) where T : IProcessor
        {
            if (metaConfig == null) throw new ArgumentNullException(nameof(metaConfig));
            T processorInstance = default(T);
            if (metaConfig.Parts.Any(p => p.Type == ProcessorMetaPartsTypeEnum.App))
            {
                processorInstance = (T)ObjectInstanceCreator.Create(
                    metaConfig.Parts.First(p => p.Type == ProcessorMetaPartsTypeEnum.App).FullTypeString,
                    null,
                    JsonDocument.Parse(metaConfig.Parts.FirstOrDefault(p => p.Type == ProcessorMetaPartsTypeEnum.App)
                        .ParametersJsonArrayStr ?? "[]").RootElement.EnumerateArray().ToArray(),
                    dependencies);
                if (processorInstance == null)
                    throw new ArgumentException("Failed for resolving AppProcessor from Type: " + metaConfig.Parts.First(p => p.Type == ProcessorMetaPartsTypeEnum.App).FullTypeString);
            }
            else if (metaConfig.Parts.Any(p => p.Type == ProcessorMetaPartsTypeEnum.DeviceHandler))
            {
                var deviceHandlerInstance = ObjectInstanceCreator.Create(metaConfig.Parts.FirstOrDefault(p => p.Type == ProcessorMetaPartsTypeEnum.DeviceHandler).FullTypeString,
                    null,
                    JsonDocument.Parse(metaConfig.Parts.First(p => p.Type == ProcessorMetaPartsTypeEnum.DeviceHandler)
                        .ParametersJsonArrayStr ?? "[]").RootElement.EnumerateArray().ToArray(),
                    dependencies);
                if (deviceHandlerInstance == null)
                    throw new ArgumentException("Failed for resolving deviceHandlerInstance from Type: " + metaConfig.Parts.FirstOrDefault(p => p.Type == ProcessorMetaPartsTypeEnum.DeviceHandler).FullTypeString);

                #region Create Parts of RawMessage Type

                Type rawMessageType = null;
                if (!string.IsNullOrEmpty(metaConfig?.Parts?.FirstOrDefault(p => p.Type == ProcessorMetaPartsTypeEnum.RawMessageTypeStr)?.FullTypeString))
                {
                    rawMessageType = Type.GetType(metaConfig?.Parts?.FirstOrDefault(p => p.Type == ProcessorMetaPartsTypeEnum.RawMessageTypeStr)?.FullTypeString);
                }
                else
                {
                    rawMessageType = deviceHandlerInstance.GetType().GetInterfaces().Where(i => i.Name == typeof(IDeviceHandler<,>).Name).FirstOrDefault()?.GetGenericArguments()?.FirstOrDefault();
                }

                if (rawMessageType == null)
                    throw new ArgumentException("Failed for resolving MetaParts: RawMessageType from Type: " + deviceHandlerInstance.GetType().FullName);

                #endregion

                #region Create Parts of Message Type

                Type messageType = null;
                if (!string.IsNullOrEmpty(metaConfig?.Parts?.FirstOrDefault(p => p.Type == ProcessorMetaPartsTypeEnum.MessageTypeStr)?.FullTypeString))
                {
                    messageType = Type.GetType(metaConfig?.Parts?.FirstOrDefault(p => p.Type == ProcessorMetaPartsTypeEnum.MessageTypeStr)?.FullTypeString);
                }
                else
                {
                    messageType = deviceHandlerInstance.GetType().GetInterfaces().Where(i => i.Name == typeof(IDeviceHandler<,>).Name).FirstOrDefault()?.GetGenericArguments()?.LastOrDefault();
                }

                if (messageType == null)
                    throw new ArgumentException("Failed for resolving MetaParts: MessageType from Type: " + deviceHandlerInstance.GetType().FullName);

                #endregion

                #region Create Parts of Parser Instance

                object parserInstance = null;
                if (!string.IsNullOrEmpty(metaConfig?.Parts?.FirstOrDefault(p => p.Type == ProcessorMetaPartsTypeEnum.Parser)?.FullTypeString))
                {
                    parserInstance = ObjectInstanceCreator.Create(metaConfig.Parts.FirstOrDefault(p => p.Type == ProcessorMetaPartsTypeEnum.Parser).FullTypeString,
                        null,
                        JsonDocument.Parse(metaConfig.Parts.FirstOrDefault(p => p.Type == ProcessorMetaPartsTypeEnum.Parser).ParametersJsonArrayStr ?? "[]").RootElement.EnumerateArray().ToArray(),
                        dependencies);
                }
                else
                {
                    var metaPartsAttri = deviceHandlerInstance.GetType().GetCustomAttributes<MetaPartsRequired>()?.FirstOrDefault(at => typeof(ParserBase).IsAssignableFrom(at.RequiredPartsType));
                    if (metaPartsAttri == null)
                    {
                        var searchedParserType = deviceHandlerInstance.GetType().Assembly.GetTypes()
                            .FirstOrDefault(t => typeof(ParserBase).IsAssignableFrom(t));
                        if (searchedParserType == null)
                            throw new ArgumentException("Failed to Search Parser from Type: " + deviceHandlerInstance.GetType().FullName);
                        else
                            parserInstance = ObjectInstanceCreator.Create(searchedParserType?.AssemblyQualifiedName,
                                null,
                                new JsonElement[] { },
                                dependencies);
                    }
                    else
                        parserInstance = ObjectInstanceCreator.Create(metaPartsAttri.RequiredPartsType.FullName,
                            null,
                            metaPartsAttri.DefaultCtorParamsJsonStrings.Select(js => JsonDocument.Parse(js).RootElement).ToArray(),
                            //JsonDocument.Parse(metaPartsAttri.DefaultCtorParamsJsonStrings ?? "[]").RootElement.EnumerateArray().ToArray(),
                            dependencies);
                }

                if (parserInstance == null)
                    throw new ArgumentException("Failed for resolving Parser from Type: " + deviceHandlerInstance.GetType().FullName);

                #endregion

                #region Create Parts of MessageCutter Instance

                object messageCutterInstance = null;
                if (!string.IsNullOrEmpty(metaConfig?.Parts?.FirstOrDefault(p => p.Type == ProcessorMetaPartsTypeEnum.MessageCutter)?.FullTypeString))
                {
                    messageCutterInstance = ObjectInstanceCreator.Create(metaConfig.Parts.FirstOrDefault(p => p.Type == ProcessorMetaPartsTypeEnum.MessageCutter).FullTypeString,
                        null,
                        JsonDocument.Parse(metaConfig.Parts.FirstOrDefault(p => p.Type == ProcessorMetaPartsTypeEnum.MessageCutter).ParametersJsonArrayStr ?? "[]").RootElement.EnumerateArray().ToArray(),
                        dependencies);
                }
                else
                {
                    var metaPartsAttri = deviceHandlerInstance.GetType().GetCustomAttributes<MetaPartsRequired>()?.FirstOrDefault(at => at.RequiredPartsType?.GetInterfaces().Any(i => i.Name == typeof(IMessageCutter<>).Name) ?? false);
                    if (metaPartsAttri == null)
                    {
                        var searchedMessageCutterType = deviceHandlerInstance.GetType().Assembly.GetTypes()
                            .FirstOrDefault(t => t.GetInterfaces().Any(i => i.Name == typeof(IMessageCutter<>).Name));
                        if (searchedMessageCutterType == null)
                            throw new ArgumentException("Failed to Search MessageCutter from Type: " + deviceHandlerInstance.GetType().FullName);
                        else
                            messageCutterInstance = ObjectInstanceCreator.Create(searchedMessageCutterType.AssemblyQualifiedName,
                                null,
                                new JsonElement[] { },
                                dependencies);
                    }
                    else
                        messageCutterInstance = ObjectInstanceCreator.Create(metaPartsAttri.RequiredPartsType.FullName,
                            null,
                            metaPartsAttri.DefaultCtorParamsJsonStrings.Select(js => JsonDocument.Parse(js).RootElement).ToArray(),
                            dependencies);

                    if (messageCutterInstance == null)
                        throw new ArgumentException("Failed for resolving MessageCutter from Type: " + deviceHandlerInstance.GetType().FullName);
                }

                #endregion

                var communicatorInstance = ObjectInstanceCreator.Create(metaConfig.Parts.FirstOrDefault(p => p.Type == ProcessorMetaPartsTypeEnum.Communicator).FullTypeString,
                    new Type[] { messageType },
                    JsonDocument.Parse(metaConfig.Parts.FirstOrDefault(p => p.Type == ProcessorMetaPartsTypeEnum.Communicator).ParametersJsonArrayStr ?? "[]").RootElement.EnumerateArray().ToArray(),
                    dependencies.Concat(new object[] { parserInstance, messageCutterInstance }).ToArray());

                if (communicatorInstance == null)
                    throw new ArgumentException("Failed for resolving Communicator from Type: " + deviceHandlerInstance.GetType().FullName);

                #region Create Final Processor Instance

                if (!string.IsNullOrEmpty(metaConfig?.Parts?.FirstOrDefault(p => p.Type == ProcessorMetaPartsTypeEnum.DeviceProcessor)?.FullTypeString))
                {
                    //if (Type.GetType(metaConfig.Parts.First(p => p.Type == ProcessorPartsTypeEnum.Processor).FullTypeString).IsGenericType)
                    //{
                    processorInstance = (T)ObjectInstanceCreator.Create(metaConfig.Parts.First(p => p.Type == ProcessorMetaPartsTypeEnum.DeviceProcessor).FullTypeString,
                        new Type[] { rawMessageType, messageType },
                        JsonDocument.Parse(metaConfig.Parts.First(p => p.Type == ProcessorMetaPartsTypeEnum.DeviceProcessor).ParametersJsonArrayStr ?? "[]").RootElement.EnumerateArray().ToArray(),
                        dependencies.Concat(new object[] { parserInstance, messageCutterInstance, communicatorInstance, deviceHandlerInstance }).ToArray());
                    //}
                    //else
                    //{
                    //    processorInstance = (T)Create(metaConfig.Parts.First(p => p.Type == ProcessorPartsTypeEnum.Processor).FullTypeString,
                    //        null,
                    //        JsonDocument.Parse(metaConfig.Parts.First(p => p.Type == ProcessorPartsTypeEnum.Processor).ParametersJsonArrayStr ?? "[]").RootElement.EnumerateArray().ToArray(),
                    //        parts.Concat(new object[] { parserInstance, messageCutterInstance, communicatorInstance, deviceHandlerInstance }).ToArray());
                    //}
                }
                else
                {
                    var metaPartsAttri = deviceHandlerInstance.GetType().GetCustomAttributes<MetaPartsRequired>()?.FirstOrDefault(at => at.RequiredPartsType.GetInterfaces().Any(i => i.Name == typeof(IDeviceProcessor<,>).Name));
                    if (metaPartsAttri == null)
                        throw new ArgumentException("Failed for resolving MetaParts: DeviceProcessor from Type: " + deviceHandlerInstance.GetType().FullName);
                    processorInstance = (T)ObjectInstanceCreator.Create(metaPartsAttri.RequiredPartsType.FullName,
                        new Type[] { rawMessageType, messageType },
                        metaPartsAttri.DefaultCtorParamsJsonStrings.Select(js => JsonDocument.Parse(js).RootElement).ToArray(),
                        dependencies.Concat(new object[] { parserInstance, messageCutterInstance, communicatorInstance, deviceHandlerInstance }).ToArray());
                }

                #endregion

            }

            if (processorInstance != null)
            {
                processorInstance.MetaConfigName = metaConfig.Name;
                //processorInstance.SerialNumber = Guid.NewGuid().ToString();
                return (T)processorInstance;
            }

            throw new InvalidOperationException("Can't Create Processor for processorMetaConfig: " + metaConfig.Name + Environment.NewLine + (metaConfig.Description ?? ""));
        }
    }
}
