/*  NOTE: This code depends on the form of Qil expressions generated by XPathPatternBuilder.
         *  More specifically, it recognizes the following two patterns:
         *
         *  A) /, *, @*, text(), comment(), processing-instruction():
         *      (And* $x:(IsType RefTo LiteralType))
         *
         *  B) foo, @ns:foo, processing-instruction('foo'):
         *      (And* $x:(And (IsType RefTo LiteralType) (Eq (NameOf RefTo) LiteralQName)))
         *
         *  where all RefTo refer to 'it', and LiteralType has exactly one NodeKind bit set.
         *
         *  If one of patterns recognized, we nip $x off of the nested And sequence:
         *      (And* (And2 (And1 $x:* $y:*) $z:*))  =>  (And* (And2 $y:* $z:*))
         */
        private void NipOffTypeNameCheck()
        {
            QilBinary[] leftPath = new QilBinary[4];    // Circular buffer for last 4 And nodes
            int         idx      = -1;                  // Index of last element in leftPath
            QilNode     node     = condition;           // Walker through left path of the tree

            nodeKind = XmlNodeKindFlags.None;
            qname    = null;

            while (node.NodeType == QilNodeType.And)
            {
                node = (leftPath[++idx & 3] = (QilBinary)node).Left;
            }

            // Recognizing (IsType RefTo LiteralType)
            if (!(node.NodeType == QilNodeType.IsType))
            {
                return;
            }

            QilBinary isType = (QilBinary)node;

            if (!(isType.Left == iterator && isType.Right.NodeType == QilNodeType.LiteralType))
            {
                return;
            }

            XmlNodeKindFlags nodeKinds = isType.Right.XmlType.NodeKinds;

            if (!Bits.ExactlyOne((uint)nodeKinds))
            {
                return;
            }

            // Recognized pattern A, check for B
            QilNode x = isType;

            nodeKind = nodeKinds;
            QilBinary lastAnd = leftPath[idx & 3];

            if (lastAnd != null && lastAnd.Right.NodeType == QilNodeType.Eq)
            {
                QilBinary eq = (QilBinary)lastAnd.Right;

                // Recognizing (Eq (NameOf RefTo) LiteralQName)
                if (eq.Left.NodeType == QilNodeType.NameOf &&
                    ((QilUnary)eq.Left).Child == iterator && eq.Right.NodeType == QilNodeType.LiteralQName
                    )
                {
                    // Recognized pattern B
                    x     = lastAnd;
                    qname = (QilName)((QilLiteral)eq.Right).Value;
                    idx--;
                }
            }

            // Nip $x off the condition
            QilBinary and1 = leftPath[idx & 3];
            QilBinary and2 = leftPath[--idx & 3];

            if (and2 != null)
            {
                and2.Left = and1.Right;
            }
            else if (and1 != null)
            {
                condition = and1.Right;
            }
            else
            {
                condition = null;
            }
        }
        private bool CheckNamespaceInScope(QilBinary nd)
        {
            QilName?      ndName;
            string        prefix, ns;
            string?       prefixExisting, nsExisting;
            XPathNodeType nodeType;

            switch (nd.NodeType)
            {
            case QilNodeType.ElementCtor:
            case QilNodeType.AttributeCtor:
                ndName = nd.Left as QilName;
                if (ndName != null)
                {
                    prefix   = ndName.Prefix;
                    ns       = ndName.NamespaceUri;
                    nodeType = (nd.NodeType == QilNodeType.ElementCtor) ? XPathNodeType.Element : XPathNodeType.Attribute;
                    break;
                }

                // Not a literal name, so return false
                return(false);

            default:
                Debug.Assert(nd.NodeType == QilNodeType.NamespaceDecl);
                prefix   = (string)(QilLiteral)nd.Left;
                ns       = (string)(QilLiteral)nd.Right;
                nodeType = XPathNodeType.Namespace;
                break;
            }

            // Attribute with null namespace and xmlns:xml are always in-scope
            if (nd.NodeType == QilNodeType.AttributeCtor && ns.Length == 0 ||
                prefix == "xml" && ns == XmlReservedNs.NsXml)
            {
                XmlILConstructInfo.Write(nd).IsNamespaceInScope = true;
                return(true);
            }

            // Don't process names that are invalid
            if (!ValidateNames.ValidateName(prefix, string.Empty, ns, nodeType, ValidateNames.Flags.CheckPrefixMapping))
            {
                return(false);
            }

            // Atomize names
            prefix = _nsmgr.NameTable !.Add(prefix);
            ns     = _nsmgr.NameTable.Add(ns);

            // Determine whether namespace is already in-scope
            for (int iNmsp = 0; iNmsp < _cntNmsp; iNmsp++)
            {
                _nsmgr.GetNamespaceDeclaration(iNmsp, out prefixExisting, out nsExisting);

                // If prefix is already declared,
                if ((object)prefix == (object?)prefixExisting)
                {
                    // Then if the namespace is the same, this namespace is redundant
                    if ((object)ns == (object?)nsExisting)
                    {
                        XmlILConstructInfo.Write(nd).IsNamespaceInScope = true;
                    }

                    // Else quit searching, because any further matching prefixes will be hidden (not in-scope)
                    Debug.Assert(nd.NodeType != QilNodeType.NamespaceDecl || !_nsmgr.HasNamespace(prefix) || _nsmgr.LookupNamespace(prefix) == ns,
                                 "Compilers must ensure that namespace declarations do not conflict with the namespace used by the element constructor.");
                    break;
                }
            }

            // If not in-scope, then add if it's allowed
            if (_addInScopeNmsp)
            {
                _nsmgr.AddNamespace(prefix, ns);
                _cntNmsp++;
            }

            return(true);
        }