protected void ComputeMirrorIndices(ModuleConstraintsModel data)
        {
            int3[] offsets = data.socketsOffsets;
            int3[] mirrors = data.socketsMirrors;

            if (offsets.Length != mirrors.Length)
            {
                Debug.LogError("Offset & Mirrors don't have the same length.", data);
                return;
            }

            int
                count = offsets.Length,
                index = -1;

            int[] indices = new int[count];

            for (int i = 0; i < count; i++)
            {
                index      = System.Array.IndexOf(offsets, mirrors[i]);
                indices[i] = index;
            }

            data.socketMirrorIndices = indices;
        }
        public ModuleConstraints FixData(ModuleConstraintsModel model)
        {
            m_data.index  = m_index;
            m_data.prefab = m_prefab;
            m_data.weight = m_weight;

            ModuleConstraintsBuilder builder;
            List <int>
            positions   = m_neighbors.keyList,
                indices = new List <int>();

            int iCount = model.socketsOffsets.Length;

            int[]
            begin = new int[iCount],
            lengths = new int[iCount];

            int count, index = 0;

            for (int i = 0; i < iCount; i++)
            {
                if (m_neighbors.TryGet(i, out List <ModuleConstraintsBuilder> builders))
                {
                    count = builders.Count;
                }
                else
                {
                    count = 0;
                }

                begin[i]   = index;
                lengths[i] = count;

                for (int b = 0; b < count; b++)
                {
                    builder = builders[b];
                    if (builder == null)
                    {
                        indices.Add(SlotContent.NULL);
                    }
                    else
                    {
                        indices.Add(builder.m_index);
                    }

                    index++;
                }
            }

            m_data.begin     = begin;
            m_data.lengths   = lengths;
            m_data.neighbors = indices.ToArray();

            return(m_data);
        }
        protected void AssignSocketColors(ModuleConstraintsModel data)
        {
            int count = data.socketsOffsets.Length;

            data.socketColors = new Color[count];

            for (int i = 0; i < count; i++)
            {
                data.socketColors[i] = new Color(UnityEngine.Random.value, UnityEngine.Random.value, UnityEngine.Random.value, 1f);
            }
        }
        protected void SetCrossSockets(ModuleConstraintsModel data)
        {
            int3[] sockets = new int3[6];

            for (int i = 0; i < 6; i++)
            {
                sockets[i] = Sockets.OFFSETS[i];
            }

            data.socketsOffsets = sockets;
            MirrorBaseSockets(data);
        }
        protected void MirrorBaseSockets(ModuleConstraintsModel data)
        {
            int3[] sockets = data.socketsOffsets;
            int3[] mirrors = new int3[data.socketsOffsets.Length];

            for (int i = 0; i < mirrors.Length; i++)
            {
                mirrors[i] = sockets[i] * -1;
            }

            data.socketsMirrors = mirrors;
            ComputeMirrorIndices(data);
        }
        protected bool CheckColors(ModuleConstraintsModel data)
        {
            if (data.socketsOffsets == null)
            {
                return(false);
            }

            if (data.socketColors == null || data.socketColors.Length != data.socketsOffsets.Length)
            {
                AssignSocketColors(data);
                return(true);
            }
            else
            {
                return(false);
            }
        }
        protected void SetN1Offsets(ModuleConstraintsModel data)
        {
            int3[] sockets = new int3[26];
            int    i       = 0;

            for (int x = -1; x < 2; x++)
            {
                for (int y = -1; y < 2; y++)
                {
                    for (int z = -1; z < 2; z++)
                    {
                        if (x == 0 && y == 0 && z == 0)
                        {
                            continue;
                        }
                        sockets[i++] = int3(x, y, z);
                    }
                }
            }

            data.socketsOffsets = sockets;
            MirrorBaseSockets(data);
        }
        public void Process(ModuleConstraintsModel inputModel, string id)
        {
            Reset();

            model = inputModel;

            m_socketsOffsets = model.socketsOffsets;
            m_socketsMirrors = model.socketsMirrors;
            m_socketCount    = m_socketsOffsets.Length;

            #region group processing

            ModuleSampler[] samplerComponent = GameObject.FindObjectsOfType <ModuleSampler>();
            ModuleSampler   sampler;

            ByteTrio
                itemCoords,
                socketCoord;

            GameObject
                samplerGo,
                itemGo,
                modulePrefab;

            Transform
                samplerTr,
                itemTr;

            int3
                samplerSize;

            ModuleConstraintsBuilder moduleBuilder;
            ModuleInfos moduleInfos;

            int itemCount;

            for (int samplerIndex = 0, count = samplerComponent.Length; samplerIndex < count; samplerIndex++)
            {
                sampler   = samplerComponent[samplerIndex];
                samplerGo = sampler.gameObject;

                if (!samplerGo.activeSelf ||
                    !sampler.enabled ||
                    sampler.manifestID != id)
                {
                    continue;
                }

                samplerSize = sampler.gridSize;
                samplerTr   = samplerGo.transform;

                m_moduleBuilders.Clear();
                m_moduleBuildersMap.Clear();

                m_items.Clear();
                m_items.Capacity = samplerTr.childCount;

                m_itemsMap.Clear();

                itemCount = samplerTr.childCount;

                // Go through all children and cache necessary data
                // such as each GameObject related prefab, coordinates in grid etc.
                for (int i = 0; i < itemCount; i++)
                {
                    itemTr = samplerTr.GetChild(i);
                    itemGo = itemTr.gameObject;

                    moduleInfos  = itemGo.GetComponent <ModuleInfos>();
                    modulePrefab = PrefabUtility.GetCorrespondingObjectFromSource <GameObject>(itemGo);

                    if (modulePrefab == null)
                    {
                        continue;
                    }

                    if (!sampler.brain.TryGetCoordOf(itemTr.position, out itemCoords))
                    {
                        Debug.LogWarning(itemGo.name + " is outside its parent grid.", itemGo);
                        continue;
                    }

                    if (!m_builderMap.TryGetValue(modulePrefab, out moduleBuilder))
                    {
                        moduleBuilder = Pool.Rent <ModuleConstraintsBuilder>();

                        moduleBuilder.m_index  = uid++;
                        moduleBuilder.m_prefab = modulePrefab;

                        m_builderMap[modulePrefab] = moduleBuilder;
                        m_builders.Add(moduleBuilder);
                    }

                    if (moduleInfos != null)
                    {
                        moduleBuilder.m_weight = moduleInfos.weight;
                    }

                    m_items.Add(itemGo);
                    m_itemsMap[itemGo]          = modulePrefab;
                    m_moduleBuildersMap[itemGo] = itemCoords;

                    m_moduleBuilders.Add(itemCoords, itemGo);
                }

                itemCount = m_items.Count;

                for (int i = 0; i < itemCount; i++)
                {
                    itemGo        = m_items[i];
                    itemCoords    = m_moduleBuildersMap[itemGo];
                    moduleBuilder = m_builderMap[m_itemsMap[itemGo]];

                    for (int s = 0; s < m_socketCount; s++)
                    {
                        socketCoord = itemCoords + m_socketsOffsets[s];

                        if (socketCoord.x < 0 || socketCoord.x >= samplerSize.x ||
                            socketCoord.y < 0 || socketCoord.y >= samplerSize.y ||
                            socketCoord.z < 0 || socketCoord.z >= samplerSize.z)
                        {
                            // If coordinate falls outside, fill neighbor slot with null reference
                            if (!sampler.ignoreNullSockets)
                            {
                                moduleBuilder.Add(s, null);
                            }
                        }
                        else if (m_moduleBuilders.TryGet(socketCoord, out List <GameObject> list))
                        {
                            // Feed module builder with current socket's contents
                            for (int b = 0, listCount = list.Count; b < listCount; b++)
                            {
                                moduleBuilder.Add(s, m_builderMap[m_itemsMap[list[b]]]);
                            }
                        }
                    }
                }
            }

            #endregion

            WriteData(id);

            #region Clean up

            for (int i = 0, count = m_builders.Count; i < count; i++)
            {
                m_builders[i].Release();
            }

            m_builders.Clear();
            m_builderMap.Clear();

            #endregion
        }
        public override void OnInspectorGUI()
        {
            bool dirty = false;
            ModuleConstraintsModel data = (ModuleConstraintsModel)target;

            if (data.socketsOffsets != null && data.socketsMirrors != null &&
                data.socketsOffsets.Length != data.socketsMirrors.Length)
            {
                EditorGUILayout.HelpBox("Offsets & Mirrors must have the same length.", MessageType.Error);
            }

            DrawDefaultInspector();

            if (GUILayout.Button("N x 0.5"))
            {
                SetCrossSockets(data);
                dirty = true;
            }

            if (GUILayout.Button("N x 1"))
            {
                SetN1Offsets(data);
                dirty = true;
            }

            if (GUILayout.Button("N x 2"))
            {
                SetN2Offsets(data);
                dirty = true;
            }

            GUILayout.Space(10.0f);

            if (GUILayout.Button("Compute mirror offsets"))
            {
                MirrorBaseSockets(data);
                dirty = true;
            }

            if (GUILayout.Button("Compute mirror indices"))
            {
                ComputeMirrorIndices(data);
                dirty = true;
            }

            if (GUILayout.Button("Assign socket colors"))
            {
                AssignSocketColors(data);
                dirty = true;
            }

            if (dirty)
            {
                EditorUtility.SetDirty(data);
            }

            if (CheckColors(data))
            {
                dirty = true;
            }
        }
 protected void SetN2Offsets(ModuleConstraintsModel data)
 {
     SetN1Offsets(data);
 }