<commit_before>using System;
using System.Collections.Generic;
using Svelto.DataStructures;
using Svelto.ECS.Internal;
using Svelto.ECS.NodeSchedulers;
using WeakReference = Svelto.DataStructures.WeakReference<Svelto.ECS.EnginesRoot>;

#if ENGINE_PROFILER_ENABLED && UNITY_EDITOR
using Svelto.ECS.Profiler;
#endif
#if NETFX_CORE
using System.Reflection;
#endif

namespace Svelto.ECS
{
    public sealed class EnginesRoot : IEnginesRoot, IEntityFactory
    {
        public EnginesRoot(NodeSubmissionScheduler nodeScheduler)
        {
            _nodeEngines = new Dictionary<Type, FasterList<IEngine>>();
            _activableEngines = new Dictionary<Type, FasterList<IEngine>>();
            _otherEngines = new FasterList<IEngine>();

            _engineRootWeakReference = new WeakReference(this);

            _nodesDB = new Dictionary<Type, FasterList<INode>>();
            _nodesDBdic = new Dictionary<Type, Dictionary<int, INode>>();

            _nodesToAdd = new FasterList<INode>();
            _metaNodesToAdd = new FasterList<INode>();

            _metaNodesDB = new Dictionary<Type, FasterList<INode>>();
            _sharedStructNodeLists = new SharedStructNodeLists();
            _sharedGroupedStructNodeLists = new SharedGroupedStructNodesLists();

            _internalRemove = InternalRemove;
            _internalDisable = InternalDisable;
            _internalEnable = InternalEnable;
            _internalMetaRemove = InternalMetaRemove;

            _scheduler = nodeScheduler;
            _scheduler.Schedule(SubmitNodes);

            _structNodeEngineType = typeof(IStructNodeEngine<>);
            _groupedStructNodesEngineType = typeof(IGroupedStructNodesEngine<>);
            _activableNodeEngineType = typeof(IActivableNodeEngine<>);
            
            _implementedInterfaceTypes = new Dictionary<Type, Type[]>();

#if ENGINE_PROFILER_ENABLED && UNITY_EDITOR
            GameObject debugEngineObject = new GameObject("Engine Debugger");
            debugEngineObject.gameObject.AddComponent<EngineProfilerBehaviour>();
#endif
        }

        void SubmitNodes()
        {
            int metaNodesCount = _metaNodesToAdd.Count;
            int nodesCount = _nodesToAdd.Count;

            if (metaNodesCount + nodesCount == 0) return;

            bool newNodesHaveBeenAddedWhileIterating;
            int startNodes = 0;
            int startMetaNodes = 0;
            int numberOfReenteringLoops = 0;

            do
            {
                var nodesToAdd = _nodesToAdd.ToArrayFast();

                for (int i = startNodes; i < nodesCount; i++)
                {
                    var node = nodesToAdd[i];
                    var nodeType = node.GetType();

                    AddNodeToTheDB(node, nodeType);
                    var nodeWithId = node as INodeWithID;
                    if (nodeWithId != null)
                        AddNodeToNodesDictionary(nodeWithId, nodeType);
                }

                var metaNodesToAdd = _metaNodesToAdd.ToArrayFast();

                for (int i = startMetaNodes; i < metaNodesCount; i++)
                {
                    var node = metaNodesToAdd[i];
                    var nodeType = node.GetType();

                    AddNodeToMetaDB(node, nodeType);
                    var nodeWithId = node as INodeWithID;
                    if (nodeWithId != null)
                        AddNodeToNodesDictionary(nodeWithId, nodeType);
                }

                for (int i = startNodes; i < nodesCount; i++)
                {
                    var node = nodesToAdd[i];
                    AddNodeToTheSuitableEngines(node, node.GetType());
                }

                for (int i = startMetaNodes; i < metaNodesCount; i++)
                {
                    var node = metaNodesToAdd[i];
                    AddNodeToTheSuitableEngines(node, node.GetType());
                }

                newNodesHaveBeenAddedWhileIterating =
                    _metaNodesToAdd.Count > metaNodesCount ||
                    _nodesToAdd.Count > nodesCount;

                startNodes = nodesCount;
                startMetaNodes = metaNodesCount;

                if (numberOfReenteringLoops > 5)
                    throw new Exception("possible infinite loop found creating Entities inside INodesEngine Add method, please consider building entities outside INodesEngine Add method");

                numberOfReenteringLoops++;

                metaNodesCount = _metaNodesToAdd.Count;
                nodesCount = _nodesToAdd.Count;

            } while (newNodesHaveBeenAddedWhileIterating);

            _nodesToAdd.Clear();
            _metaNodesToAdd.Clear();
        }

        public void AddEngine(IEngine engine)
        {
#if ENGINE_PROFILER_ENABLED && UNITY_EDITOR
            EngineProfiler.AddEngine(engine);
#endif
            var queryableNodeEngine = engine as IQueryableNodeEngine;
            if (queryableNodeEngine != null)
                queryableNodeEngine.nodesDB =
                    new EngineNodeDB(_nodesDB, _nodesDBdic, _metaNodesDB);

            var engineType = engine.GetType();
            var implementedInterfaces = engineType.GetInterfaces();

            CollectImplementedInterfaces(implementedInterfaces);

            var engineAdded = CheckGenericEngines(engine);

            if (CheckLegacyNodesEngine(engine, ref engineAdded) == false)
                CheckNodesEngine(engine, engineType, ref engineAdded);

            if (engineAdded == false)
                _otherEngines.Add(engine);

            var callBackOnAddEngine = engine as ICallBackOnAddEngine;
            if (callBackOnAddEngine != null)
                callBackOnAddEngine.Ready();
        }

        void CollectImplementedInterfaces(Type[] implementedInterfaces)
        {
            _implementedInterfaceTypes.Clear();

            var type = typeof(IEngine);

            for (int index = 0; index < implementedInterfaces.Length; index++)
            {
                var interfaceType = implementedInterfaces[index];

                if (interfaceType.IsAssignableFrom(type) == false)
                    continue;
#if !NETFX_CORE

                if (false == interfaceType.IsGenericType)
#else
                if (false == interfaceType.IsConstructedGenericType)
#endif
                {
                    continue;
                }

                var genericTypeDefinition = interfaceType.GetGenericTypeDefinition();

                _implementedInterfaceTypes.Add(genericTypeDefinition, interfaceType.GetGenericArguments());
            }
        }

        bool CheckGenericEngines(IEngine engine)
        {
            if (_implementedInterfaceTypes.Count == 0) return false;

            bool engineAdded = false;

            if (_implementedInterfaceTypes.ContainsKey(_structNodeEngineType))
            {
                ((IStructNodeEngine)engine).CreateStructNodes
                    (_sharedStructNodeLists);
            }

            if (_implementedInterfaceTypes.ContainsKey(_groupedStructNodesEngineType))
            {
                ((IGroupedStructNodesEngine)engine).CreateStructNodes
                    (_sharedGroupedStructNodeLists);
            }

            Type[] arguments;
            if (_implementedInterfaceTypes.TryGetValue(_activableNodeEngineType, 
                out arguments))
            {
                AddEngine(engine, arguments, _activableEngines);

                engineAdded = true;
            }

            return engineAdded;
        }

        bool CheckLegacyNodesEngine(IEngine engine, ref bool engineAdded)
        {
            var nodesEngine = engine as INodesEngine;
            if (nodesEngine != null)
            {
                AddEngine(nodesEngine, nodesEngine.AcceptedNodes(), _nodeEngines);

                engineAdded = true;

                return true;
            }

            return false;
        }

        bool CheckNodesEngine(IEngine engine, Type engineType, ref bool engineAdded)
        {
#if !NETFX_CORE
            var baseType = engineType.BaseType;
            
            if (baseType.IsGenericType
#else
                var baseType = engineType.GetTypeInfo().BaseType;

                if (baseType.IsConstructedGenericType
#endif
                && engine is INodeEngine)
            {
                AddEngine(engine as INodeEngine, baseType.GetGenericArguments(), _nodeEngines);

                engineAdded = true;

                return true;
            }

            return false;
        }

        public void BuildEntity(int ID, EntityDescriptor ed)
        {
            var entityNodes = ed.BuildNodes(ID,
                _internalRemove,
                _internalEnable,
                _internalDisable
            );

            _nodesToAdd.AddRange(entityNodes);
        }

        /// <summary>
        /// A meta entity is a way to manage a set of entitites that are not easily 
        /// queriable otherwise. For example you may want to group existing entities
        /// by size and type and then use the meta entity node to manage the data 
        /// shared among the single entities of the same type and size. This will 
        /// prevent the scenario where the coder is forced to parse all the entities to 
        /// find the ones of the same size and type. 
        /// Since the entities are managed through the shared node, the same
        /// shared node must be found on the single entities of the same type and size.
        /// The shared node of the meta entity is then used by engines that are meant 
        /// to manage a group of entities through a single node. 
        /// The same engine can manage several meta entities nodes too.
        /// The Engine manages the logic of the Meta Node data and other engines
        /// can read back this data through the normal entity as the shared node
        /// will be present in their descriptor too.
        /// </summary>
        /// <param name="metaEntityID"></param>
        /// <param name="ed"></param>
        public void BuildMetaEntity(int metaEntityID, EntityDescriptor ed)
        {
            var entityNodes = ed.BuildNodes(metaEntityID,
                _internalMetaRemove,
                _internalEnable,
                _internalDisable
                );

            _metaNodesToAdd.AddRange(entityNodes);
        }

        /// <summary>
        /// Using this function is like building a normal entity, but the nodes
        /// are grouped by groupID to be better processed inside engines and
        /// improve cache locality. Only IGroupStructNodeWithID nodes are grouped
        /// other nodes are managed as usual.
        /// </summary>
        /// <param name="entityID"></param>
        /// <param name="groupID"></param>
        /// <param name="ed"></param>
        public void BuildEntityInGroup(int entityID, int groupID, 
            EntityDescriptor ed)
        {
            var entityNodes = ed.BuildNodes(entityID,
                _internalRemove,
                _internalEnable,
                _internalDisable
                );

            _nodesToAdd.AddRange(entityNodes);

            for (int i = 0; i < entityNodes.Count; i++)
            {
                var groupNode = entityNodes[i] as IGroupedStructNodeWithID;
                if (groupNode != null)
                    groupNode.groupID = groupID;
            }
        }

        static void AddEngine(IEngine engine, Type[] types,
            Dictionary<Type, FasterList<IEngine>> engines)
        {
            for (int i = 0; i < types.Length; i++)
            {
                FasterList<IEngine> list;

                var type = types[i];

                if (engines.TryGetValue(type, out list) == false)
                {
                    list = new FasterList<IEngine>();

                    engines.Add(type, list);
                }

                list.Add(engine);
            }
        }

        void AddNodeToMetaDB(INode node, Type nodeType)
        {
            FasterList<INode> nodes;
            if (_metaNodesDB.TryGetValue(nodeType, out nodes) == false)
                nodes = _metaNodesDB[nodeType] = new FasterList<INode>();

            nodes.Add(node);
        }

        void AddNodeToTheDB<T>(T node, Type nodeType) where T : INode
        {
            FasterList<INode> nodes;
            if (_nodesDB.TryGetValue(nodeType, out nodes) == false)
                nodes = _nodesDB[nodeType] = new FasterList<INode>();

            nodes.Add(node);
        }

        void AddNodeToNodesDictionary<T>(T node, Type nodeType) where T : INodeWithID
        {
            Dictionary<int, INode> nodesDic;

            if (_nodesDBdic.TryGetValue(nodeType, out nodesDic) == false)
                nodesDic = _nodesDBdic[nodeType] = new Dictionary<int, INode>();

            nodesDic.Add(node.ID, node);
        }

        void AddNodeToTheSuitableEngines(INode node, Type nodeType)
        {
            FasterList<IEngine> enginesForNode;

            if (_nodeEngines.TryGetValue(nodeType, out enginesForNode))
            {
                for (int j = 0; j < enginesForNode.Count; j++)
                {
#if ENGINE_PROFILER_ENABLED && UNITY_EDITOR
                    EngineProfiler.MonitorAddDuration(AddNodeToEngine, enginesForNode[j] as INodeEngine, node);
#else
                    (enginesForNode[j] as INodeEngine).Add(node);
#endif
                }
            }
        }

        void RemoveNodeFromTheDB<T>(T node, Type nodeType) where T : INode
        {
            FasterList<INode> nodes;
            if (_nodesDB.TryGetValue(nodeType, out nodes) == true)
                nodes.UnorderedRemove(node); //should I remove it from the dictionary if length is zero?
        }

        void RemoveNodeFromMetaDB<T>(T node, Type nodeType) where T : INode
        {
            FasterList<INode> nodes;
            if (_metaNodesDB.TryGetValue(nodeType, out nodes) == true)
                nodes.UnorderedRemove(node); //should I remove it from the dictionary if length is zero?
        }

        void RemoveNodeFromNodesDictionary<T>(T node, Type nodeType) where T : INodeWithID
        {
            Dictionary<int, INode> nodesDic;

            if (_nodesDBdic.TryGetValue(nodeType, out nodesDic))
                nodesDic.Remove(node.ID);
        }

        void RemoveNodeFromEngines<T>(T node, Type nodeType) where T : INode
        {
            FasterList<IEngine> enginesForNode;

            if (_nodeEngines.TryGetValue(nodeType, out enginesForNode))
            {
                for (int j = 0; j < enginesForNode.Count; j++)
                {
#if ENGINE_PROFILER_ENABLED && UNITY_EDITOR
                    EngineProfiler.MonitorRemoveDuration(RemoveNodeFromEngine, (enginesForNode[j] as INodeEngine), node);
#else
                    (enginesForNode[j] as INodeEngine).Remove(node);
#endif
                }
            }
        }

        void DisableNodeFromEngines(INode node, Type nodeType)
        {
            FasterList<IEngine> enginesForNode;

            if (_activableEngines.TryGetValue(nodeType, out enginesForNode))
            {
                for (int j = 0; j < enginesForNode.Count; j++)
                {
                    (enginesForNode[j] as IActivableNodeEngine).Disable(node);
                }
            }
        }

        void EnableNodeFromEngines(INode node, Type nodeType)
        {
            FasterList<IEngine> enginesForNode;

            if (_activableEngines.TryGetValue(nodeType, out enginesForNode))
            {
                for (int j = 0; j < enginesForNode.Count; j++)
                {
                    (enginesForNode[j] as IActivableNodeEngine).Enable(node);
                }
            }
        }
#if ENGINE_PROFILER_ENABLED && UNITY_EDITOR
        void AddNodeToEngine(IEngine engine, INode node)
        {
            (engine as INodeEngine).Add(node);
        }

        void RemoveNodeFromEngine(IEngine engine, INode node)
        {
            (engine as INodeEngine).Remove(node);
        }
#endif

        void InternalDisable(FasterList<INode> nodes)
        {
            if (_engineRootWeakReference.IsValid == false)
                return;

            for (int i = 0; i < nodes.Count; i++)
            {
                var node = nodes[i];
                Type nodeType = node.GetType();
                DisableNodeFromEngines(node, nodeType);
            }
        }

        void InternalEnable(FasterList<INode> nodes)
        {
            if (_engineRootWeakReference.IsValid == false)
                return;

            for (int i = 0; i < nodes.Count; i++)
            {
                var node = nodes[i];
                Type nodeType = node.GetType();
                EnableNodeFromEngines(node, nodeType);
            }
        }

        void InternalRemove(FasterList<INode> nodes)
        {
            if (_engineRootWeakReference.IsValid == false)
                return;

            for (int i = 0; i < nodes.Count; i++)
            {
                var node = nodes[i];
                Type nodeType = node.GetType();

                RemoveNodeFromEngines(node, nodeType);
                RemoveNodeFromTheDB(node, node.GetType());

                var nodeWithId = node as INodeWithID;
                if (nodeWithId != null)
                    RemoveNodeFromNodesDictionary(nodeWithId, nodeType);
            }
        }

        void InternalMetaRemove(FasterList<INode> nodes)
        {
            for (int i = 0; i < nodes.Count; i++)
            {
                var node = nodes[i];
                Type nodeType = node.GetType();

                RemoveNodeFromEngines(node, nodeType);
                RemoveNodeFromMetaDB(node, nodeType);

                var nodeWithId = node as INodeWithID;
                if (nodeWithId != null)
                    RemoveNodeFromNodesDictionary(nodeWithId, nodeType);
            }
        }

        readonly Dictionary<Type, FasterList<IEngine>> _nodeEngines;
        readonly Dictionary<Type, FasterList<IEngine>> _activableEngines;

        readonly FasterList<IEngine> _otherEngines;

        readonly Dictionary<Type, FasterList<INode>> _nodesDB;
        readonly Dictionary<Type, FasterList<INode>> _metaNodesDB;

        readonly Dictionary<Type, Dictionary<int, INode>> _nodesDBdic;

        readonly FasterList<INode> _nodesToAdd;
        readonly FasterList<INode> _metaNodesToAdd;

        readonly WeakReference _engineRootWeakReference;
        readonly SharedStructNodeLists _sharedStructNodeLists;
        readonly SharedGroupedStructNodesLists _sharedGroupedStructNodeLists;

        readonly NodeSubmissionScheduler _scheduler;

        readonly Action<FasterList<INode>> _internalRemove;
        readonly Action<FasterList<INode>> _internalEnable;
        readonly Action<FasterList<INode>> _internalDisable;
        readonly Action<FasterList<INode>> _internalMetaRemove;

        readonly Type _structNodeEngineType;
        readonly Type _groupedStructNodesEngineType;
        readonly Type _activableNodeEngineType;

        readonly Dictionary<Type, Type[]> _implementedInterfaceTypes;
    }
}

<commit_msg>fixed mistake introduced with the last commit<commit_after>using System;
using System.Collections.Generic;
using Svelto.DataStructures;
using Svelto.ECS.Internal;
using Svelto.ECS.NodeSchedulers;
using WeakReference = Svelto.DataStructures.WeakReference<Svelto.ECS.EnginesRoot>;

#if ENGINE_PROFILER_ENABLED && UNITY_EDITOR
using Svelto.ECS.Profiler;
#endif
#if NETFX_CORE
using System.Reflection;
#endif

namespace Svelto.ECS
{
    public sealed class EnginesRoot : IEnginesRoot, IEntityFactory
    {
        public EnginesRoot(NodeSubmissionScheduler nodeScheduler)
        {
            _nodeEngines = new Dictionary<Type, FasterList<IEngine>>();
            _activableEngines = new Dictionary<Type, FasterList<IEngine>>();
            _otherEngines = new FasterList<IEngine>();

            _engineRootWeakReference = new WeakReference(this);

            _nodesDB = new Dictionary<Type, FasterList<INode>>();
            _nodesDBdic = new Dictionary<Type, Dictionary<int, INode>>();

            _nodesToAdd = new FasterList<INode>();
            _metaNodesToAdd = new FasterList<INode>();

            _metaNodesDB = new Dictionary<Type, FasterList<INode>>();
            _sharedStructNodeLists = new SharedStructNodeLists();
            _sharedGroupedStructNodeLists = new SharedGroupedStructNodesLists();

            _internalRemove = InternalRemove;
            _internalDisable = InternalDisable;
            _internalEnable = InternalEnable;
            _internalMetaRemove = InternalMetaRemove;

            _scheduler = nodeScheduler;
            _scheduler.Schedule(SubmitNodes);

            _structNodeEngineType = typeof(IStructNodeEngine<>);
            _groupedStructNodesEngineType = typeof(IGroupedStructNodesEngine<>);
            _activableNodeEngineType = typeof(IActivableNodeEngine<>);
            
            _implementedInterfaceTypes = new Dictionary<Type, Type[]>();

#if ENGINE_PROFILER_ENABLED && UNITY_EDITOR
            GameObject debugEngineObject = new GameObject("Engine Debugger");
            debugEngineObject.gameObject.AddComponent<EngineProfilerBehaviour>();
#endif
        }

        void SubmitNodes()
        {
            int metaNodesCount = _metaNodesToAdd.Count;
            int nodesCount = _nodesToAdd.Count;

            if (metaNodesCount + nodesCount == 0) return;

            bool newNodesHaveBeenAddedWhileIterating;
            int startNodes = 0;
            int startMetaNodes = 0;
            int numberOfReenteringLoops = 0;

            do
            {
                var nodesToAdd = _nodesToAdd.ToArrayFast();

                for (int i = startNodes; i < nodesCount; i++)
                {
                    var node = nodesToAdd[i];
                    var nodeType = node.GetType();

                    AddNodeToTheDB(node, nodeType);
                    var nodeWithId = node as INodeWithID;
                    if (nodeWithId != null)
                        AddNodeToNodesDictionary(nodeWithId, nodeType);
                }

                var metaNodesToAdd = _metaNodesToAdd.ToArrayFast();

                for (int i = startMetaNodes; i < metaNodesCount; i++)
                {
                    var node = metaNodesToAdd[i];
                    var nodeType = node.GetType();

                    AddNodeToMetaDB(node, nodeType);
                    var nodeWithId = node as INodeWithID;
                    if (nodeWithId != null)
                        AddNodeToNodesDictionary(nodeWithId, nodeType);
                }

                for (int i = startNodes; i < nodesCount; i++)
                {
                    var node = nodesToAdd[i];
                    AddNodeToTheSuitableEngines(node, node.GetType());
                }

                for (int i = startMetaNodes; i < metaNodesCount; i++)
                {
                    var node = metaNodesToAdd[i];
                    AddNodeToTheSuitableEngines(node, node.GetType());
                }

                newNodesHaveBeenAddedWhileIterating =
                    _metaNodesToAdd.Count > metaNodesCount ||
                    _nodesToAdd.Count > nodesCount;

                startNodes = nodesCount;
                startMetaNodes = metaNodesCount;

                if (numberOfReenteringLoops > 5)
                    throw new Exception("possible infinite loop found creating Entities inside INodesEngine Add method, please consider building entities outside INodesEngine Add method");

                numberOfReenteringLoops++;

                metaNodesCount = _metaNodesToAdd.Count;
                nodesCount = _nodesToAdd.Count;

            } while (newNodesHaveBeenAddedWhileIterating);

            _nodesToAdd.Clear();
            _metaNodesToAdd.Clear();
        }

        public void AddEngine(IEngine engine)
        {
#if ENGINE_PROFILER_ENABLED && UNITY_EDITOR
            EngineProfiler.AddEngine(engine);
#endif
            var queryableNodeEngine = engine as IQueryableNodeEngine;
            if (queryableNodeEngine != null)
                queryableNodeEngine.nodesDB =
                    new EngineNodeDB(_nodesDB, _nodesDBdic, _metaNodesDB);

            var engineType = engine.GetType();
            var implementedInterfaces = engineType.GetInterfaces();

            CollectImplementedInterfaces(implementedInterfaces);

            var engineAdded = CheckGenericEngines(engine);

            if (CheckLegacyNodesEngine(engine, ref engineAdded) == false)
                CheckNodesEngine(engine, engineType, ref engineAdded);

            if (engineAdded == false)
                _otherEngines.Add(engine);

            var callBackOnAddEngine = engine as ICallBackOnAddEngine;
            if (callBackOnAddEngine != null)
                callBackOnAddEngine.Ready();
        }

        void CollectImplementedInterfaces(Type[] implementedInterfaces)
        {
            _implementedInterfaceTypes.Clear();

            var type = typeof(IEngine);

            for (int index = 0; index < implementedInterfaces.Length; index++)
            {
                var interfaceType = implementedInterfaces[index];

                if (type.IsAssignableFrom(interfaceType) == false)
                    continue;
#if !NETFX_CORE

                if (false == interfaceType.IsGenericType)
#else
                if (false == interfaceType.IsConstructedGenericType)
#endif
                {
                    continue;
                }

                var genericTypeDefinition = interfaceType.GetGenericTypeDefinition();

                _implementedInterfaceTypes.Add(genericTypeDefinition, interfaceType.GetGenericArguments());
            }
        }

        bool CheckGenericEngines(IEngine engine)
        {
            if (_implementedInterfaceTypes.Count == 0) return false;

            bool engineAdded = false;

            if (_implementedInterfaceTypes.ContainsKey(_structNodeEngineType))
            {
                ((IStructNodeEngine)engine).CreateStructNodes
                    (_sharedStructNodeLists);
            }

            if (_implementedInterfaceTypes.ContainsKey(_groupedStructNodesEngineType))
            {
                ((IGroupedStructNodesEngine)engine).CreateStructNodes
                    (_sharedGroupedStructNodeLists);
            }

            Type[] arguments;
            if (_implementedInterfaceTypes.TryGetValue(_activableNodeEngineType, 
                out arguments))
            {
                AddEngine(engine, arguments, _activableEngines);

                engineAdded = true;
            }

            return engineAdded;
        }

        bool CheckLegacyNodesEngine(IEngine engine, ref bool engineAdded)
        {
            var nodesEngine = engine as INodesEngine;
            if (nodesEngine != null)
            {
                AddEngine(nodesEngine, nodesEngine.AcceptedNodes(), _nodeEngines);

                engineAdded = true;

                return true;
            }

            return false;
        }

        bool CheckNodesEngine(IEngine engine, Type engineType, ref bool engineAdded)
        {
#if !NETFX_CORE
            var baseType = engineType.BaseType;
            
            if (baseType.IsGenericType
#else
                var baseType = engineType.GetTypeInfo().BaseType;

                if (baseType.IsConstructedGenericType
#endif
                && engine is INodeEngine)
            {
                AddEngine(engine as INodeEngine, baseType.GetGenericArguments(), _nodeEngines);

                engineAdded = true;

                return true;
            }

            return false;
        }

        public void BuildEntity(int ID, EntityDescriptor ed)
        {
            var entityNodes = ed.BuildNodes(ID,
                _internalRemove,
                _internalEnable,
                _internalDisable
            );

            _nodesToAdd.AddRange(entityNodes);
        }

        /// <summary>
        /// A meta entity is a way to manage a set of entitites that are not easily 
        /// queriable otherwise. For example you may want to group existing entities
        /// by size and type and then use the meta entity node to manage the data 
        /// shared among the single entities of the same type and size. This will 
        /// prevent the scenario where the coder is forced to parse all the entities to 
        /// find the ones of the same size and type. 
        /// Since the entities are managed through the shared node, the same
        /// shared node must be found on the single entities of the same type and size.
        /// The shared node of the meta entity is then used by engines that are meant 
        /// to manage a group of entities through a single node. 
        /// The same engine can manage several meta entities nodes too.
        /// The Engine manages the logic of the Meta Node data and other engines
        /// can read back this data through the normal entity as the shared node
        /// will be present in their descriptor too.
        /// </summary>
        /// <param name="metaEntityID"></param>
        /// <param name="ed"></param>
        public void BuildMetaEntity(int metaEntityID, EntityDescriptor ed)
        {
            var entityNodes = ed.BuildNodes(metaEntityID,
                _internalMetaRemove,
                _internalEnable,
                _internalDisable
                );

            _metaNodesToAdd.AddRange(entityNodes);
        }

        /// <summary>
        /// Using this function is like building a normal entity, but the nodes
        /// are grouped by groupID to be better processed inside engines and
        /// improve cache locality. Only IGroupStructNodeWithID nodes are grouped
        /// other nodes are managed as usual.
        /// </summary>
        /// <param name="entityID"></param>
        /// <param name="groupID"></param>
        /// <param name="ed"></param>
        public void BuildEntityInGroup(int entityID, int groupID, 
            EntityDescriptor ed)
        {
            var entityNodes = ed.BuildNodes(entityID,
                _internalRemove,
                _internalEnable,
                _internalDisable
                );

            _nodesToAdd.AddRange(entityNodes);

            for (int i = 0; i < entityNodes.Count; i++)
            {
                var groupNode = entityNodes[i] as IGroupedStructNodeWithID;
                if (groupNode != null)
                    groupNode.groupID = groupID;
            }
        }

        static void AddEngine(IEngine engine, Type[] types,
            Dictionary<Type, FasterList<IEngine>> engines)
        {
            for (int i = 0; i < types.Length; i++)
            {
                FasterList<IEngine> list;

                var type = types[i];

                if (engines.TryGetValue(type, out list) == false)
                {
                    list = new FasterList<IEngine>();

                    engines.Add(type, list);
                }

                list.Add(engine);
            }
        }

        void AddNodeToMetaDB(INode node, Type nodeType)
        {
            FasterList<INode> nodes;
            if (_metaNodesDB.TryGetValue(nodeType, out nodes) == false)
                nodes = _metaNodesDB[nodeType] = new FasterList<INode>();

            nodes.Add(node);
        }

        void AddNodeToTheDB<T>(T node, Type nodeType) where T : INode
        {
            FasterList<INode> nodes;
            if (_nodesDB.TryGetValue(nodeType, out nodes) == false)
                nodes = _nodesDB[nodeType] = new FasterList<INode>();

            nodes.Add(node);
        }

        void AddNodeToNodesDictionary<T>(T node, Type nodeType) where T : INodeWithID
        {
            Dictionary<int, INode> nodesDic;

            if (_nodesDBdic.TryGetValue(nodeType, out nodesDic) == false)
                nodesDic = _nodesDBdic[nodeType] = new Dictionary<int, INode>();

            nodesDic.Add(node.ID, node);
        }

        void AddNodeToTheSuitableEngines(INode node, Type nodeType)
        {
            FasterList<IEngine> enginesForNode;

            if (_nodeEngines.TryGetValue(nodeType, out enginesForNode))
            {
                for (int j = 0; j < enginesForNode.Count; j++)
                {
#if ENGINE_PROFILER_ENABLED && UNITY_EDITOR
                    EngineProfiler.MonitorAddDuration(AddNodeToEngine, enginesForNode[j] as INodeEngine, node);
#else
                    (enginesForNode[j] as INodeEngine).Add(node);
#endif
                }
            }
        }

        void RemoveNodeFromTheDB<T>(T node, Type nodeType) where T : INode
        {
            FasterList<INode> nodes;
            if (_nodesDB.TryGetValue(nodeType, out nodes) == true)
                nodes.UnorderedRemove(node); //should I remove it from the dictionary if length is zero?
        }

        void RemoveNodeFromMetaDB<T>(T node, Type nodeType) where T : INode
        {
            FasterList<INode> nodes;
            if (_metaNodesDB.TryGetValue(nodeType, out nodes) == true)
                nodes.UnorderedRemove(node); //should I remove it from the dictionary if length is zero?
        }

        void RemoveNodeFromNodesDictionary<T>(T node, Type nodeType) where T : INodeWithID
        {
            Dictionary<int, INode> nodesDic;

            if (_nodesDBdic.TryGetValue(nodeType, out nodesDic))
                nodesDic.Remove(node.ID);
        }

        void RemoveNodeFromEngines<T>(T node, Type nodeType) where T : INode
        {
            FasterList<IEngine> enginesForNode;

            if (_nodeEngines.TryGetValue(nodeType, out enginesForNode))
            {
                for (int j = 0; j < enginesForNode.Count; j++)
                {
#if ENGINE_PROFILER_ENABLED && UNITY_EDITOR
                    EngineProfiler.MonitorRemoveDuration(RemoveNodeFromEngine, (enginesForNode[j] as INodeEngine), node);
#else
                    (enginesForNode[j] as INodeEngine).Remove(node);
#endif
                }
            }
        }

        void DisableNodeFromEngines(INode node, Type nodeType)
        {
            FasterList<IEngine> enginesForNode;

            if (_activableEngines.TryGetValue(nodeType, out enginesForNode))
            {
                for (int j = 0; j < enginesForNode.Count; j++)
                {
                    (enginesForNode[j] as IActivableNodeEngine).Disable(node);
                }
            }
        }

        void EnableNodeFromEngines(INode node, Type nodeType)
        {
            FasterList<IEngine> enginesForNode;

            if (_activableEngines.TryGetValue(nodeType, out enginesForNode))
            {
                for (int j = 0; j < enginesForNode.Count; j++)
                {
                    (enginesForNode[j] as IActivableNodeEngine).Enable(node);
                }
            }
        }
#if ENGINE_PROFILER_ENABLED && UNITY_EDITOR
        void AddNodeToEngine(IEngine engine, INode node)
        {
            (engine as INodeEngine).Add(node);
        }

        void RemoveNodeFromEngine(IEngine engine, INode node)
        {
            (engine as INodeEngine).Remove(node);
        }
#endif

        void InternalDisable(FasterList<INode> nodes)
        {
            if (_engineRootWeakReference.IsValid == false)
                return;

            for (int i = 0; i < nodes.Count; i++)
            {
                var node = nodes[i];
                Type nodeType = node.GetType();
                DisableNodeFromEngines(node, nodeType);
            }
        }

        void InternalEnable(FasterList<INode> nodes)
        {
            if (_engineRootWeakReference.IsValid == false)
                return;

            for (int i = 0; i < nodes.Count; i++)
            {
                var node = nodes[i];
                Type nodeType = node.GetType();
                EnableNodeFromEngines(node, nodeType);
            }
        }

        void InternalRemove(FasterList<INode> nodes)
        {
            if (_engineRootWeakReference.IsValid == false)
                return;

            for (int i = 0; i < nodes.Count; i++)
            {
                var node = nodes[i];
                Type nodeType = node.GetType();

                RemoveNodeFromEngines(node, nodeType);
                RemoveNodeFromTheDB(node, node.GetType());

                var nodeWithId = node as INodeWithID;
                if (nodeWithId != null)
                    RemoveNodeFromNodesDictionary(nodeWithId, nodeType);
            }
        }

        void InternalMetaRemove(FasterList<INode> nodes)
        {
            for (int i = 0; i < nodes.Count; i++)
            {
                var node = nodes[i];
                Type nodeType = node.GetType();

                RemoveNodeFromEngines(node, nodeType);
                RemoveNodeFromMetaDB(node, nodeType);

                var nodeWithId = node as INodeWithID;
                if (nodeWithId != null)
                    RemoveNodeFromNodesDictionary(nodeWithId, nodeType);
            }
        }

        readonly Dictionary<Type, FasterList<IEngine>> _nodeEngines;
        readonly Dictionary<Type, FasterList<IEngine>> _activableEngines;

        readonly FasterList<IEngine> _otherEngines;

        readonly Dictionary<Type, FasterList<INode>> _nodesDB;
        readonly Dictionary<Type, FasterList<INode>> _metaNodesDB;

        readonly Dictionary<Type, Dictionary<int, INode>> _nodesDBdic;

        readonly FasterList<INode> _nodesToAdd;
        readonly FasterList<INode> _metaNodesToAdd;

        readonly WeakReference _engineRootWeakReference;
        readonly SharedStructNodeLists _sharedStructNodeLists;
        readonly SharedGroupedStructNodesLists _sharedGroupedStructNodeLists;

        readonly NodeSubmissionScheduler _scheduler;

        readonly Action<FasterList<INode>> _internalRemove;
        readonly Action<FasterList<INode>> _internalEnable;
        readonly Action<FasterList<INode>> _internalDisable;
        readonly Action<FasterList<INode>> _internalMetaRemove;

        readonly Type _structNodeEngineType;
        readonly Type _groupedStructNodesEngineType;
        readonly Type _activableNodeEngineType;

        readonly Dictionary<Type, Type[]> _implementedInterfaceTypes;
    }
}


{'fraction_non_alphanumeric': 0.07862420647309813, 'fraction_numerical': 0.0008845873660110313, 'mean_word_length': 1.7826685006877578, 'pattern_counts': {'":': 0, '<': 197, '<?xml version=': 0, '>': 173, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2835290', 'n_tokens_mistral': 10167, 'n_tokens_neox': 9585, 'n_words': 2362}