void BuildCoroutineNode(CoroutineNodeModel node, RoslynEcsTranslator translator, ref int currentIndex)
        {
            foreach (var variable in node.Fields.Where(v => !m_ComponentVariables.ContainsKey(v.Key)))
            {
                m_ComponentVariables.Add(variable.Key, variable.Value);
            }

            // Coroutine initialization state
            var initState = m_States[currentIndex];

            initState.Statements.AddRange(CoroutineTranslator.BuildInitState(node, translator));

            var nextStateIndex = initState.NextStateIndex;

            initState.NextStateIndex = m_States.Count;

            // Coroutine update state
            var updateState = RequestNewState();

            updateState.SkipStateBuilding = true;

            var hasNextNode = node.ParentStackModel.NodeModels.Last() != node;
            var nextStack   = GetNextStack(node.ParentStackModel);
            int nextIndex;

            if (hasNextNode || nextStack != null && !m_StackIndexes.ContainsKey(nextStack))
            {
                nextIndex = m_States.Count;
                var newState = RequestNewState();
                newState.NextStateIndex = nextStateIndex;
            }
            else
            {
                nextIndex = nextStateIndex == 0 ? m_States.Count : nextStateIndex;
            }

            node.NextStateIndex = nextIndex;
            updateState.Statements.AddRange(translator.BuildNode(node).Cast <StatementSyntax>());
            currentIndex = GetCurrentStateIndex();
        }
        void BuildIfConditionNode(IfConditionNodeModel node, RoslynEcsTranslator translator, int stateIndex)
        {
            translator.BuildNode(node);

            var firstThenStack = RoslynTranslator.GetConnectedStack(node, 0);
            var firstElseStack = RoslynTranslator.GetConnectedStack(node, 1);
            var ifState        = m_States[stateIndex];
            var ifIndex        = GetCurrentStateIndex();

            var endStackIndex = 0;

            if (translator.EndStack != null)
            {
                m_StackIndexes.TryGetValue(translator.EndStack, out endStackIndex);
            }

            // Reserve then/else/complete states first
            var       thenIndex = ifIndex;
            var       thenBlock = Block().AddStatements(ReturnStatement(LiteralExpression(SyntaxKind.FalseLiteralExpression)));
            StateData thenState = null;

            if (firstThenStack != null)
            {
                if (firstThenStack == translator.EndStack && endStackIndex != 0)
                {
                    thenBlock = Block().AddStatements(BuildGoToState(endStackIndex));
                }
                else
                {
                    thenIndex += 1;
                    thenState  = RequestNewState();
                    TryAddStackIndex(firstThenStack, thenIndex);
                    thenBlock = Block().AddStatements(BuildGoToState(thenIndex));
                }
            }

            var       elseIndex = thenIndex + 1;
            var       elseBlock = Block().AddStatements(ReturnStatement(LiteralExpression(SyntaxKind.FalseLiteralExpression)));
            StateData elseState = null;

            if (firstElseStack != null)
            {
                if (firstElseStack == translator.EndStack && endStackIndex != 0)
                {
                    elseBlock = Block().AddStatements(BuildGoToState(endStackIndex));
                }
                else
                {
                    elseState = RequestNewState();
                    TryAddStackIndex(firstElseStack, elseIndex);
                    elseBlock = Block().AddStatements(BuildGoToState(elseIndex));
                }
            }

            // Then Build stacks
            ifState.Statements.Add(RoslynBuilder.IfStatement(
                                       translator.BuildPort(node.IfPort).SingleOrDefault(),
                                       thenBlock,
                                       elseBlock)
                                   .WithAdditionalAnnotations(
                                       new SyntaxAnnotation(Annotations.VSNodeMetadata, node.Guid.ToString())));
            ifState.Statements.Add(ReturnStatement(LiteralExpression(SyntaxKind.TrueLiteralExpression)));
            ifState.SkipStateBuilding = true;

            var reserveEndStackState = translator.EndStack != null &&
                                       translator.EndStack != firstElseStack &&
                                       translator.EndStack != firstThenStack &&
                                       endStackIndex == 0;

            if (reserveEndStackState)
            {
                var endState = RequestNewState();
                endState.NextStateIndex = GetNextStateIndex(translator.EndStack);
                TryAddStackIndex(translator.EndStack, GetCurrentStateIndex());
            }

            var origBuiltStacks = translator.BuiltStacks;

            translator.BuiltStacks = new HashSet <IStackModel>(origBuiltStacks);

            if (translator.EndStack != firstThenStack)
            {
                if (translator.EndStack != null && thenState != null)
                {
                    thenState.NextStateIndex = m_StackIndexes[translator.EndStack];
                }
                BuildStack(translator, firstThenStack, thenIndex, StackExitStrategy.Inherit);
            }

            var partialStacks = translator.BuiltStacks;

            translator.BuiltStacks = new HashSet <IStackModel>(origBuiltStacks);

            if (translator.EndStack != firstElseStack)
            {
                if (translator.EndStack != null && elseState != null)
                {
                    elseState.NextStateIndex = m_StackIndexes[translator.EndStack];
                }
                BuildStack(translator, firstElseStack, elseIndex, StackExitStrategy.Inherit);
            }

            translator.BuiltStacks.UnionWith(partialStacks);
        }
        protected void BuildStack(RoslynEcsTranslator translator, IStackModel stack, int currentStateIndex,
                                  StackExitStrategy exitStrategy = StackExitStrategy.Return)
        {
            if (stack == null || stack.State == ModelState.Disabled)
            {
                return;
            }

            translator.RegisterBuiltStack(stack);

            if (m_StackIndexes.TryGetValue(stack, out var endStackIndex))
            {
                currentStateIndex = endStackIndex;
            }

            // JUST in case... until we validate the previous failsafe
            if (m_BuiltStackCounter++ > 10000)
            {
                throw new InvalidOperationException("Infinite loop while building the script, aborting");
            }

            var origStackExitStrategy = m_StackExitStrategy;

            if (exitStrategy != StackExitStrategy.Inherit)
            {
                m_StackExitStrategy = exitStrategy;
            }

            if (m_States.Count == 0)
            {
                var state = RequestNewState();
                state.NextStateIndex = GetCurrentStateIndex();
            }

            var origEndStack = translator.EndStack;

            foreach (var node in stack.NodeModels)
            {
                if (node.State == ModelState.Disabled)
                {
                    continue;
                }

                switch (node)
                {
                case CoroutineNodeModel coroutineNode:
                    BuildCoroutineNode(coroutineNode, translator, ref currentStateIndex);
                    continue;

                case IfConditionNodeModel ifConditionNodeModel:
                    BuildIfConditionNode(ifConditionNodeModel, translator, currentStateIndex);
                    continue;

                default:
                {
                    var blocks       = translator.BuildNode(node);
                    var currentState = m_States[currentStateIndex];
                    currentState.SkipStateBuilding = currentState.SkipStateBuilding || SkipStateBuilding;
                    currentState.Statements.AddRange(ConvertNodesToSyntaxList(node, blocks, translator.Options));

                    if (!SkipStateBuilding && stack.NodeModels.Last() == node && !HasConnectedStack(stack))
                    {
                        currentState.SkipStateBuilding = true;
                        currentState.Statements.Add(ReturnStatement(
                                                        LiteralExpression(SyntaxKind.FalseLiteralExpression)));
                    }

                    SkipStateBuilding = false;
                    break;
                }
                }
            }

            if (stack.DelegatesOutputsToNode(out _))
            {
                var nextStack = translator.EndStack;
                m_StackExitStrategy = origStackExitStrategy;

                if (translator.EndStack == origEndStack)
                {
                    return;
                }

                translator.EndStack = origEndStack;

                if (nextStack != null)
                {
                    BuildStack(translator, nextStack, m_StackIndexes[nextStack], exitStrategy);
                }

                return;
            }

            foreach (var outputPort in stack.OutputPorts)
            {
                foreach (var connectedStack in outputPort.ConnectionPortModels)
                {
                    if (connectedStack.NodeModel is IStackModel nextStack)
                    {
                        if (!ReferenceEquals(nextStack, translator.EndStack))
                        {
                            BuildStack(translator, nextStack, currentStateIndex, exitStrategy);
                        }
                    }
                }
            }

            m_StackExitStrategy = origStackExitStrategy;
        }