public static List <List <string> > NodeListToStrings(List <Node> _nodes)
        {
            List <List <string> > nodes_as_strings = new List <List <string> >();

            if (_nodes == null)
            {
                return(nodes_as_strings);
            }

            foreach (Node n in _nodes)
            {
                List <string> node_record = NodeAssembler.GetNodeRecordSimple(n);
                nodes_as_strings.Add(node_record);
            }

            return(nodes_as_strings);
        }
        public static List <Node> GetNodeListExplicit(List <List <string> > _string_data)
        {
            List <Node>           nodes       = new List <Node>();
            List <string>         nodes_names = new List <string>();
            List <List <string> > nodes_contained_node_names = new List <List <string> >();
            List <int>            nodes_levels = new List <int>();

            // trim the input
            List <List <string> > string_data_clean = NodeAssembler.CutEmptyColumnsAndRows(_string_data);

            // extract INFO
            NodeAssembler.ExtractInfoFromNodeStrings(string_data_clean, NR_FIELDS, out nodes, out nodes_names,
                                                     out nodes_contained_node_names, out nodes_levels);
            // detect CONTAINMENT relationships
            List <Node> nodes_FINAL = NodeAssembler.ConvertExplicitNodeAndLevelInfoToNodeList(nodes, nodes_levels);

            return(nodes_FINAL);
        }
        private static List <List <string> > GetSubNodeRecords(Node _node, int _node_level, bool _explicit = false)
        {
            List <List <string> > subnode_records = new List <List <string> >();

            if (_node == null)
            {
                return(subnode_records);
            }
            if (_node.ContainedNodes == null || _node.ContainedNodes.Count < 1)
            {
                return(subnode_records);
            }

            foreach (Node subNode in _node.ContainedNodes)
            {
                if (!_explicit)
                {
                    if (subNode.SyncByName && !subNode.HasNonSyncNodes())
                    {
                        continue;
                    }
                }

                List <string> subnode_record = NodeAssembler.GetNodeRecordSimple(subNode);
                int           subNode_level  = _node_level + 1;
                subnode_record.Add(subNode_level.ToString());
                subnode_records.Add(subnode_record);

                List <List <string> > subnode_contained_records = NodeAssembler.GetSubNodeRecords(subNode, subNode_level, _explicit);
                if (subnode_contained_records.Count > 0)
                {
                    subnode_records.AddRange(subnode_contained_records);
                }
            }

            return(subnode_records);
        }
        public static List <List <string> > NodeListToStringsComplete(List <Node> _nodes, bool _explicit = false)
        {
            List <List <string> > nodes_as_strings = new List <List <string> >();

            if (_nodes == null)
            {
                return(nodes_as_strings);
            }

            foreach (Node n in _nodes)
            {
                List <string> node_record = NodeAssembler.GetNodeRecordSimple(n);
                node_record.Add("0"); // level
                nodes_as_strings.Add(node_record);

                List <List <string> > node_contained_records = NodeAssembler.GetSubNodeRecords(n, 0, _explicit);
                if (node_contained_records.Count > 0)
                {
                    nodes_as_strings.AddRange(node_contained_records);
                }
            }

            return(nodes_as_strings);
        }
        /// <summary>
        /// For parsing EXCEL Input. Handles the finalization of a processed node.
        /// </summary>
        /// <param name="_n">the node to be finalized (no unresolved subnodes remaining)</param>
        /// <param name="_level">the Level of the node in the _nodeTree_FIN</param>
        /// <param name="_nodeTree_FIN">Tree of finalized nodes</param>
        /// <param name="_nodeList_FIN">List of unique synched finalized nodes</param>
        /// <param name="_nodeStack_OPEN">stack of unfinished nodes</param>
        /// <param name="_nodeLevelStack_OPEN">stackof the levels of the unfinished nodes</param>
        /// <param name="_nodeContNamesStack_OPEN">stack of the not yet preocessed subnode names of the unfinished nodes</param>
        /// <param name="_ids_finalized">the IDs of the finalized nodes (due to the tree structure there can be more than just _n)</param>
        private static void FinalizeNode(Node _n, int _level, ref List <Node> _nodeTree_FIN, ref List <Node> _nodeList_FIN,
                                         ref Stack <Node> _nodeStack_OPEN, ref Stack <int> _nodeLevelStack_OPEN, ref Stack <List <string> > _nodeContNamesStack_OPEN,
                                         ref List <long> _ids_finalized)
        {
            if (_n == null || _nodeTree_FIN == null || _nodeList_FIN == null ||
                _nodeStack_OPEN == null || _nodeLevelStack_OPEN == null || _nodeContNamesStack_OPEN == null ||
                _ids_finalized == null)
            {
                return;
            }

            int nrNodesOnStack = _nodeStack_OPEN.Count;

            if (nrNodesOnStack != _nodeLevelStack_OPEN.Count || nrNodesOnStack != _nodeContNamesStack_OPEN.Count)
            {
                return;
            }

            // 0: mark the node as finalized
            _ids_finalized.Add(_n.ID);

            // 1: remove node from the unfinished stacks, if it was on them
            if (nrNodesOnStack > 0)
            {
                Node n_on_top = _nodeStack_OPEN.Peek();
                if (n_on_top.ID == _n.ID)
                {
                    _nodeStack_OPEN.Pop();
                    _nodeLevelStack_OPEN.Pop();
                    _nodeContNamesStack_OPEN.Pop();
                }
            }

            // 2: add node to the list of unique synced finished nodes
            NodeAssembler.AddNodeToListofFinishedNodes(_n, ref _nodeList_FIN);

            // 3: ATTACH node
            if (_level == NodeAssembler.NODE_ROOT_LEVEL)
            {
                // add node to the tree of finalized nodes
                _nodeTree_FIN.Add(_n);
            }
            else
            {
                // attach to a node on the unfinished stacks ONE LEVEL higher
                if (_nodeStack_OPEN.Count > 0)
                {
                    int           nL_at_top_of_stack  = _nodeLevelStack_OPEN.Peek();
                    List <string> nCN_at_top_of_stack = _nodeContNamesStack_OPEN.Peek();
                    if (_level == nL_at_top_of_stack + 1 && nCN_at_top_of_stack.Contains(_n.NodeName))
                    {
                        // add the finalized LOWER-LEVEL node to the most recent node ONE LEVEL HIGHER
                        Node n_at_top_of_stack = _nodeStack_OPEN.Peek();
                        n_at_top_of_stack.AddNode(_n);
                        nCN_at_top_of_stack.Remove(_n.NodeName);
                        // check to see if the node ONE LEVEL HIGHER has just been finished
                        if (nCN_at_top_of_stack.Count == 0)
                        {
                            NodeAssembler.FinalizeNode(n_at_top_of_stack, nL_at_top_of_stack,
                                                       ref _nodeTree_FIN, ref _nodeList_FIN,
                                                       ref _nodeStack_OPEN, ref _nodeLevelStack_OPEN, ref _nodeContNamesStack_OPEN,
                                                       ref _ids_finalized);
                        }
                    }
                }
            }
        }
        private static List <Node> ConvertNodeAndLevelInfoToNodeList(List <Node> _nodes_at_allLevels, List <string> _nodes_names,
                                                                     List <List <string> > _nodes_contained_node_names, List <int> _nodes_levels)
        {
            List <Node> nodeTree_FIN = new List <Node>(); // contains all finished nodes of level 0
            List <Node> nodeList_FIN = new List <Node>(); // contains UNIQUE SYNCED FINISHED nodes of various levels

            Stack <Node>           nodeStack_OPEN          = new Stack <Node>();
            Stack <int>            nodeLevelStack_OPEN     = new Stack <int>();
            Stack <List <string> > nodeContNamesStack_OPEN = new Stack <List <string> >();

            List <long> ids_remaining = _nodes_at_allLevels.Select(x => x.ID).ToList();

            int nrR          = ids_remaining.Count;
            int nrTotal      = nrR;
            int nrIterations = 0;

            //string debug = "";
            while (nrR > 0 && nrIterations < NodeAssembler.MAX_NR_ITERATIONS)
            {
                for (int i = 0; i < nrTotal; i++)
                {
                    // take current node, if not processed yet
                    if (!ids_remaining.Contains(_nodes_at_allLevels[i].ID))
                    {
                        continue;
                    }
                    Node          n   = _nodes_at_allLevels[i];
                    int           nL  = _nodes_levels[i];
                    List <string> nCN = _nodes_contained_node_names[i];
                    // add node to the open stack
                    nodeStack_OPEN.Push(n);
                    nodeLevelStack_OPEN.Push(nL);
                    nodeContNamesStack_OPEN.Push(nCN);

                    #region DEBUG
                    //debug += "i=" + i + " taken: " + _nodes_at_allLevels[i].NodeName + " at L" + _nodes_levels[i] + "\n";
                    //debug += "FINISHED TREE\n";
                    //foreach (Node ntf in nodeTree_FIN)
                    //{
                    //    debug += ntf.ToString() + "\n";
                    //}
                    //debug += "\nFINISHED NODES\n";
                    //foreach (Node ntl in nodeList_FIN)
                    //{
                    //    debug += ntl.ToString() + "\n";
                    //}
                    //debug += "\nOPEN STACK\n";
                    //foreach (Node nso in nodeStack_OPEN)
                    //{
                    //    debug += nso.ToString() + "\n";
                    //}
                    //debug += "\n";
                    #endregion

                    // PROCESS NODE -> try to resolve the contained node names
                    if (nCN.Count != 0)
                    {
                        List <string> to_remove = new List <string>();
                        int           index_last_following_CN = i + 1;
                        foreach (string name in nCN)
                        {
                            // the name is not from an explicit node definition -> produce it here
                            if (!_nodes_names.Contains(name))
                            {
                                Node subN = new Node(name, string.Empty, Node.NO_DATA, Node.NO_DATA, Node.NO_DATA, true, false);
                                nodeTree_FIN.Add(subN);
                                NodeAssembler.AddNodeToListofFinishedNodes(subN, ref nodeList_FIN);
                                n.AddNode(subN);
                                to_remove.Add(name);
                                continue;
                            }

                            // check if there isn't an unprocessed node FOLLOWING in the input node list
                            if (nrIterations == 0 && index_last_following_CN <= nrTotal - 1)
                            {
                                int  index;
                                bool found = NodeAssembler.LookAhead(name, nL + 1, _nodes_at_allLevels, _nodes_levels,
                                                                     index_last_following_CN, out index);
                                if (found)
                                {
                                    index_last_following_CN = index + 1;
                                    continue;
                                    // ... and wait for it
                                }
                            }

                            // contained node has not been processed yet -> wait
                            int ind = nodeList_FIN.FindIndex(x => x.NodeName == name);
                            if (ind < 0 || ind >= nodeList_FIN.Count)
                            {
                                continue;
                            }

                            // add a DEEP copy of the found node as a subnode to the current one
                            Node copy = new Node(nodeList_FIN[ind], true);
                            copy.NodeSource = "Copy";
                            n.AddNode(copy);
                            to_remove.Add(name);
                            //debug += "made deep copy\n\n";
                        }
                        foreach (string name in to_remove)
                        {
                            nCN.Remove(name);
                        }
                    }

                    // FINALIZE NODE
                    if (nCN.Count == 0)
                    {
                        List <long> ids_finalized = new List <long>();
                        NodeAssembler.FinalizeNode(n, nL, ref nodeTree_FIN, ref nodeList_FIN,
                                                   ref nodeStack_OPEN, ref nodeLevelStack_OPEN, ref nodeContNamesStack_OPEN,
                                                   ref ids_finalized);
                        foreach (long id in ids_finalized)
                        {
                            ids_remaining.Remove(id);
                        }
                    }
                }
                nrIterations++;
                nrR = ids_remaining.Count;
                nodeStack_OPEN.Clear();
                nodeLevelStack_OPEN.Clear();
                nodeContNamesStack_OPEN.Clear();
            }

            return(nodeTree_FIN);
        }