private List <Comment[]> ExtractThreads(Comment rootComment, string authorUsername)
        {
            List <Comment[]> ret = new List <Comment[]>();

            // Create a tree.
            TreeNode <Comment> tree = new TreeNode <Comment>(rootComment, null);

            AddChildren(tree);

            // Links that compose threads.
            LinkStorage <TreeNode <Comment> > ls = new LinkStorage <TreeNode <Comment> >();

            CreateLinksBasedOnAuthorComments(tree, ls, authorUsername);
            AddMissingLinksBetweenAdjacentOnes(tree, ls);
            ReSetParentsForContinuousReplies(tree, ls);

            // This composes final threads.
            IEnumerable <List <Comment> > threads = EnumerateThreads(tree, ls).ToArray();

            foreach (IEnumerable <Comment> iec in threads)
            {
                Comment[] thread = iec.ToArray();
                ret.Add(thread);
            }

            return(ret);
        }
        /// <summary>Goes through comment tree. If two links are adjacent and there are no others,
        /// add a missing link between them.</summary>
        /// <param name="tree">Comment tree.</param>
        /// <param name="ls">Links storage.</param>
        private void AddMissingLinksBetweenAdjacentOnes(TreeNode <Comment> tree, LinkStorage <TreeNode <Comment> > ls)
        {
            Tuple <TreeNode <Comment>, TreeNode <Comment> >[] allLinks = ls.EnumerateLinks().ToArray();

            foreach (var link in allLinks)
            {
                // Child node of (A - B), B.
                TreeNode <Comment> child = link.Item2;

                // Shouldn't have direct children. No (B - C) links.
                bool hasDirectChildren = ls.GetLinksWithTop(child).Where(z => z.Item1 != z.Item2).Any();
                if (hasDirectChildren)
                {
                    continue;
                }

                // First adjacent link, starting from C, (C - D).
                Tuple <TreeNode <Comment>, TreeNode <Comment> > singleAdjacentLink = child.Children.Select(ls.GetLinksWithTop).SelectMany(a => a).FirstOrDefault();
                if (singleAdjacentLink != null)
                {
                    // OK, create a link between (B - C).
                    // Now we have chain (A - B), (B - C), (C - D).
                    ls.AddLink(child, singleAdjacentLink.Item1);

                    // Is (A - B) actually (B - B)? If so, remove it.
                    if (link.Item1 == link.Item2)
                    {
                        ls.RemoveLink(link.Item1, link.Item2);
                    }
                }
            }
        }
        private void CreateLinksBasedOnAuthorComments(TreeNode <Comment> root, LinkStorage <TreeNode <Comment> > ls, string authorUsername)
        {
            foreach (TreeNode <Comment> node in root)
            {
                Comment c        = node.Data;
                bool    isAuthor = c.Poster.Username == authorUsername;

                if (isAuthor)
                {
                    // Since it's author, mark comments before and after (if the same poster).
                    Comment cUber = null;
                    if (node.Parent != null)
                    {
                        cUber = node.Parent.Data as Comment;
                    }

                    if (cUber != null)
                    {
                        // Add link for parent.
                        ls.AddLink(node.Parent, node);
                        string previousUsername = cUber.Poster.Username;

                        TreeNode <Comment>[] childrenOfSamePrevious = node.Children
                                                                      .Where(z => SuitableForTaking(z.Data, previousUsername))
                                                                      .ToArray();

                        // Add link for all children of the same poster. Most likely, count is <= 1.
                        foreach (TreeNode <Comment> cUnter in childrenOfSamePrevious)
                        {
                            ls.AddLink(node, cUnter);
                        }
                    }
                    else
                    {
                        // Just the parent comment.
                        // Single element link.
                        ls.AddLink(node, node);
                    }
                }
            }
        }
        private void ReSetParentsForContinuousReplies(TreeNode <Comment> tree, LinkStorage <TreeNode <Comment> > ls)
        {
            var allLinksByParent = ls.EnumerateLinks().GroupBy(z => z.Item1);

            foreach (var group in allLinksByParent)
            {
                // (A - B), (A - C).
                Tuple <TreeNode <Comment>, TreeNode <Comment> >[] fromThis = group.ToArray();
                for (int i = 1; i < fromThis.Length; i++)
                {
                    // No (B - ...)?
                    bool previousHasOtherLinks = ls.GetLinksWithTop(fromThis[i - 1].Item2).Any();
                    if (previousHasOtherLinks)
                    {
                        continue;
                    }

                    // Then remove (A - C), set (B - C).
                    ls.RemoveLink(fromThis[i].Item1, fromThis[i].Item2);
                    ls.AddLink(fromThis[i - 1].Item2, fromThis[i].Item2);
                }
            }
        }
        private IEnumerable <List <Comment> > EnumerateThreads(TreeNode <Comment> rootComment, LinkStorage <TreeNode <Comment> > ls)
        {
            HashSet <TreeNode <Comment> > wentThrough = new HashSet <TreeNode <Comment> >();

            // This enumerates all nodes.
            foreach (TreeNode <Comment> node in rootComment)
            {
                if (wentThrough.Contains(node))
                {
                    continue;
                }
                wentThrough.Add(node);

                List <Comment> thread = new List <Comment>();

                // First thread that starts from this node.
                Tuple <TreeNode <Comment>, TreeNode <Comment> > tuple = ls.GetLinksWithTop(node).FirstOrDefault();
                if (tuple != null)
                {
                    // OK, there is a link that starts from this node.

                    // First node.
                    AddCommentToList(thread, tuple.Item1);
                    while (tuple != null)
                    {
                        // Second node.
                        if (tuple.Item1 != tuple.Item2)
                        {
                            AddCommentToList(thread, tuple.Item2);
                            wentThrough.Add(tuple.Item2);
                        }

                        // Go deeper.
                        tuple = ls.GetLinksWithTop(tuple.Item2).Where(z => z.Item1 != z.Item2).FirstOrDefault();
                    }
                }
                else
                {
                    // There weren't any links starting from this one.
                    // Does any end on it?

                    tuple = ls.GetLinkWithBottom(node);

                    if (tuple != null)
                    {
                        thread.Add(tuple.Item2.Data);
                    }
                }

                // Save thread if not empty.
                if (thread.Count > 0)
                {
                    yield return(thread);
                }
            }

            yield break;
        }