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

// <code>EntityReference</code> objects may be inserted into the structure 
// model when an entity reference is in the source document, or when the user 
// wishes to insert an entity reference. Note that  character references and 
// references to predefined entities are considered to be expanded by the 
// HTML or XML processor so that characters are represented by their Unicode 
// equivalent rather than by an entity reference. Moreover, the XML  
// processor may completely expand references to entities while building the 
// structure model, instead of providing <code>EntityReference</code> 
// objects. If it does provide such objects, then for a given 
// <code>EntityReference</code> node, it may be that there is no 
// <code>Entity</code> node representing the referenced entity; but if such 
// an <code>Entity</code> exists, then the child list of the 
// <code>EntityReference</code> node is the same as that of the 
// <code>Entity</code> node. As with the <code>Entity</code> node, all 
// descendants of the <code>EntityReference</code> are readonly.
// <p>The resolution of the children of the <code>EntityReference</code> (the  
// replacement value of the referenced <code>Entity</code>) may be lazily  
// evaluated; actions by the user (such as calling the  
// <code>childNodes</code> method on the <code>EntityReference</code> node)  
// are assumed to trigger the evaluation.

namespace System.Xml {

    using System.Diagnostics;

    // Represents an entity reference node.
    public class XmlEntityReference : XmlLinkedNode {
        string name;
        XmlLinkedNode lastChild;

        protected internal XmlEntityReference( string name, XmlDocument doc ) : base( doc ) {
            if ( !doc.IsLoading ) {
                if ( name.Length > 0 && name[0] == '#' ) {
                    throw new ArgumentException( Res.GetString( Res.Xdom_InvalidCharacter_EntityReference ) );
                }
            }
            this.name = doc.NameTable.Add(name);
            doc.fEntRefNodesPresent = true;
        }

        // Gets the name of the node.
        public override string Name {
            get { return name;}
        }

        // Gets the name of the node without the namespace prefix.
        public override string LocalName {
            get { return name;}
        }

        // Gets or sets the value of the node.
        public override String Value { 
            get {
                return null;
            }

            set {
                throw new InvalidOperationException(Res.GetString(Res.Xdom_EntRef_SetVal));
            }
        }

        // Gets the type of the node.
        public override XmlNodeType NodeType {
            get { return XmlNodeType.EntityReference;}
        }

        // Creates a duplicate of this node.
        public override XmlNode CloneNode(bool deep) {
            Debug.Assert( OwnerDocument != null );
            XmlEntityReference eref = OwnerDocument.CreateEntityReference( name );
            return eref;
        }

        //
        // Microsoft extensions
        //

        // Gets a value indicating whether the node is read-only.
        public override bool IsReadOnly {
            get { 
                return true;        // Make entity references readonly
            }
        }

        internal override bool IsContainer {
            get { return true;}
        }

        internal override void SetParent( XmlNode node ) {
            base.SetParent(node);
            if ( LastNode == null && node != null && node != OwnerDocument ) {
                //first time insert the entity reference into the tree, we should expand its children now
                XmlLoader loader = new XmlLoader();
                loader.ExpandEntityReference(this);
                
            }
        }

        internal override void SetParentForLoad( XmlNode node ) {
         this.SetParent(  node );
        }

        internal override XmlLinkedNode LastNode {
            get { 
                return lastChild;
            }
            set { lastChild = value;}
        }

        internal override bool IsValidChildType( XmlNodeType type ) {
            switch (type) {
                case XmlNodeType.Element:
                case XmlNodeType.Text:
                case XmlNodeType.EntityReference:
                case XmlNodeType.Comment:
                case XmlNodeType.Whitespace:
                case XmlNodeType.SignificantWhitespace:
                case XmlNodeType.ProcessingInstruction:
                case XmlNodeType.CDATA:
                    return true;

                default:
                    return false;
            }
        }

        // Saves the node to the specified XmlWriter.
        public override void WriteTo(XmlWriter w) {
            w.WriteEntityRef(name);
        }

        // Saves all the children of the node to the specified XmlWriter.
        public override void WriteContentTo(XmlWriter w) {
            // -- eventually will the fix. commented out waiting for finalizing on the issue.
            foreach( XmlNode n in this ) {
                n.WriteTo( w );
            } //still use the old code to generate the output
            /*
            foreach( XmlNode n in this ) {
                if ( n.NodeType != XmlNodeType.EntityReference )
                n.WriteTo( w );
                else
                    n.WriteContentTo( w );
            }*/
        }

        public override String BaseURI {
            get {
                return OwnerDocument.BaseURI;
            }
        }

        private string ConstructBaseURI( string baseURI, string systemId ) {
            if ( baseURI == null )
                return systemId;
            int nCount = baseURI.LastIndexOf('/')+1;
            string buf = baseURI;
            if ( nCount > 0 && nCount < baseURI.Length )
                buf = baseURI.Substring(0, nCount);
            else if ( nCount == 0 )
                buf = buf + "\\";
            return (buf + systemId.Replace('\\', '/')); 
        }

        //childrenBaseURI returns where the entity reference node's children come from
        internal String ChildBaseURI {
            get {
                //get the associate entity and return its baseUri
                XmlEntity ent = OwnerDocument.GetEntityNode( name );
                if ( ent != null ) {
                    if ( ent.SystemId != null && ent.SystemId.Length > 0 )
                        return ConstructBaseURI(ent.BaseURI, ent.SystemId); 
                    else
                        return ent.BaseURI;
                }
                return String.Empty;
            }
        }
    }
}
