//------------------------------------------------------------------------------
// <copyright file="UnionExpr.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>                                                                
// <owner current="true" primary="true">[....]</owner>
//------------------------------------------------------------------------------

namespace MS.Internal.Xml.XPath {
    using System;
    using System.Xml;
    using System.Xml.XPath;
    using System.Diagnostics;
    using System.Xml.Xsl;
    
    internal sealed class UnionExpr : Query {
        internal Query qy1, qy2;
        private bool advance1, advance2;
        private XPathNavigator currentNode;
        private XPathNavigator nextNode;

        public UnionExpr(Query query1, Query query2) {
            this.qy1 = query1;
            this.qy2 = query2;
            this.advance1 = true;
            this.advance2 = true;
        }
        private UnionExpr(UnionExpr other) : base(other) {
            this.qy1 = Clone(other.qy1);
            this.qy2 = Clone(other.qy2);
            this.advance1 = other.advance1;
            this.advance2 = other.advance2;
            this.currentNode = Clone(other.currentNode);
            this.nextNode    = Clone(other.nextNode);
        }
        
        public override void Reset() {
            qy1.Reset();
            qy2.Reset();
            advance1 = true;
            advance2 = true;
            nextNode = null;
        }

        public override void SetXsltContext(XsltContext xsltContext) {
            qy1.SetXsltContext(xsltContext);
            qy2.SetXsltContext(xsltContext);
        }

        public override object Evaluate(XPathNodeIterator context) {
            qy1.Evaluate(context);
            qy2.Evaluate(context);
            advance1 = true;
            advance2 = true;
            nextNode = null;
            base.ResetCount();
            return this;
        }

        private XPathNavigator ProcessSamePosition(XPathNavigator result){
           currentNode = result;
           advance1 = advance2 = true;
           return result;
        }

        private XPathNavigator ProcessBeforePosition(XPathNavigator res1, XPathNavigator res2){
            nextNode = res2;
            advance2 = false;
            advance1 = true;
            currentNode = res1;
            return res1;
        }

        private XPathNavigator ProcessAfterPosition(XPathNavigator res1, XPathNavigator res2){
            nextNode = res1;
            advance1 = false;
            advance2 = true;
            currentNode = res2;
            return res2;
        } 
        
        public override XPathNavigator Advance() {
            XPathNavigator res1, res2;
            XmlNodeOrder order = 0;
            if (advance1) {
                res1 = qy1.Advance();
            } else {
                res1 = nextNode;
            }
            if (advance2) {
                res2 = qy2.Advance();
            } else {
                res2 = nextNode;
            }
            if (res1 != null && res2 != null) {
                order = CompareNodes(res1, res2);
            } else if (res2 == null) {
                advance1 = true;
                advance2 = false;
                currentNode = res1;
                nextNode = null;
                return res1;
            } else {
                advance1 = false;
                advance2 = true;
                currentNode = res2;
                nextNode = null;
                return res2;
            }

            if (order == XmlNodeOrder.Before) {
                return ProcessBeforePosition(res1, res2);
            } else if (order == XmlNodeOrder.After) {
                return ProcessAfterPosition(res1, res2);
            } else {
                // BugBug. In case of Unknown we sorting as the same.
                return ProcessSamePosition(res1);
            }
        }

        public override XPathNavigator MatchNode(XPathNavigator xsltContext) {
            if (xsltContext != null) {
                XPathNavigator result = qy1.MatchNode(xsltContext);
                if (result != null) {
                    return result;
                }
                return qy2.MatchNode(xsltContext);
            }
            return null;
        }

        public override XPathResultType StaticType { get { return XPathResultType.NodeSet; } }

        public override XPathNodeIterator Clone() { return new UnionExpr(this); }

        public override XPathNavigator Current { get { return currentNode; } }
        public override int CurrentPosition { get { throw new InvalidOperationException(); } }

        public override void PrintQuery(XmlWriter w) {
            w.WriteStartElement(this.GetType().Name);
            if (qy1 != null) {
                qy1.PrintQuery(w);
            }
            if (qy2 != null) {
                qy2.PrintQuery(w);
            }
            w.WriteEndElement();
        }
    }
}
