/// <summary>
        /// Inserts a text between two nodes
        /// </summary>
        internal static void InsertTextBetweenTwoNodes(XmlCursorPos cursorPos, System.Xml.XmlNode nodeBefore, System.Xml.XmlNode nodeAfter, string text, XmlRules xmlRules)
        {
            if (ToolboxXml.IsTextOrCommentNode(nodeBefore))  // if the node is already text before, then simply append to it
            {
                nodeBefore.InnerText += text;
                cursorPos.SetPos(nodeBefore, XmlCursorPositions.CursorInsideTextNode, nodeBefore.InnerText.Length);
            }
            else  // the node before is no text
            {
                if (ToolboxXml.IsTextOrCommentNode(nodeAfter))  // if the node behind it is already text then just paste it into
                {
                    nodeAfter.InnerText = $"{text}{nodeAfter.InnerText}";
                    cursorPos.SetPos(nodeAfter, XmlCursorPositions.CursorInsideTextNode, text.Length);
                }
                else // the node behind is also no text
                {
                    // Insert between two non-text nodes
                    if (xmlRules.IsThisTagAllowedAtThisPos("#PCDATA", cursorPos))
                    {
                        System.Xml.XmlText newTextNode = cursorPos.ActualNode.OwnerDocument.CreateTextNode(text);
                        InsertXmlNode(cursorPos, newTextNode, xmlRules, false);
                    }
                    else
                    {
#warning Insert another correct message or sound
                        Debug.Assert(false, "Error - Beep!");
                    }
                }
            }
        }
        /// <summary>
        /// Inserts the specified text at the current cursor position, if possible
        /// </summary>
        internal static InsertTextResult InsertText(XmlCursorPos cursorPos, string rawText, XmlRules xmlRules)
        {
            // Revise the entered text in preprocessing if necessary.
            // In an AIML 1.1 DTD this can mean, for example, that the text for insertion into the PATTERN tag is changed to upper case
            string text = xmlRules.InsertTextTextPreProcessing(rawText, cursorPos, out System.Xml.XmlNode replacementNode);

            if (replacementNode != null)
            {
                // If the entered text has become a node instead, e.g. with AIML,
                // if you press * in a template and then want to insert a <star> instead
                return(new InsertTextResult {
                    ReplaceNode = replacementNode
                });
            }

            switch (cursorPos.PosOnNode)
            {
            case XmlCursorPositions.CursorOnNodeStartTag:
            case XmlCursorPositions.CursorOnNodeEndTag:
                // First check whether this node may be replaced by a text node
                if (xmlRules.IsThisTagAllowedAtThisPos("#PCDATA", cursorPos))
                {
                    // The selected node by a newly created text node
                    System.Xml.XmlText neuerTextNode = cursorPos.ActualNode.OwnerDocument.CreateTextNode(text);
                    cursorPos.ActualNode.ParentNode.ReplaceChild(cursorPos.ActualNode, neuerTextNode);
                    cursorPos.SetPos(neuerTextNode, XmlCursorPositions.CursorBehindTheNode);
                }
                throw new ApplicationException(String.Format("InsertText: unknown CursorPos {0}", cursorPos.PosOnNode));

            case XmlCursorPositions.CursorBehindTheNode:
                InsertTextBetweenTwoNodes(cursorPos, cursorPos.ActualNode, cursorPos.ActualNode.NextSibling, text, xmlRules);
                break;

            case XmlCursorPositions.CursorInFrontOfNode:
                InsertTextBetweenTwoNodes(cursorPos, cursorPos.ActualNode.PreviousSibling, cursorPos.ActualNode, text, xmlRules);
                break;

            case XmlCursorPositions.CursorInsideTheEmptyNode:
                // First check if text is allowed inside the empty node
                if (xmlRules.IsThisTagAllowedAtThisPos("#PCDATA", cursorPos))
                {
                    // Then create a text node within the empty node with the desired text content
                    System.Xml.XmlText newTextNode = cursorPos.ActualNode.OwnerDocument.CreateTextNode(text);
                    cursorPos.ActualNode.AppendChild(newTextNode);
                    cursorPos.SetPos(newTextNode, XmlCursorPositions.CursorBehindTheNode);
                }
                else
                {
                    // Error BEEEEP!
                }
                break;

            case XmlCursorPositions.CursorInsideTextNode:
                string textBeforeNode  = cursorPos.ActualNode.InnerText.Substring(0, cursorPos.PosInTextNode);
                string textAfterCursor = cursorPos.ActualNode.InnerText.Substring(cursorPos.PosInTextNode, cursorPos.ActualNode.InnerText.Length - cursorPos.PosInTextNode);
                // Insert the character of the pressed keys after the cursor
                cursorPos.ActualNode.InnerText = $"{textBeforeNode}{text}{textAfterCursor}";
                cursorPos.SetPos(cursorPos.ActualNode, cursorPos.PosOnNode, cursorPos.PosInTextNode + text.Length);
                break;

            default:
                throw new ApplicationException(String.Format("InsertText: unknown CursorPos {0}", cursorPos.PosOnNode));
            }
            return(new InsertTextResult {
                ReplaceNode = replacementNode
            });
        }