//////////////////////////////////////////////////////////////////////////////
//
//  Microsoft Research Singularity
//
//  Copyright (c) Microsoft Corporation.  All rights reserved.
//
//  File:   Libraries\Xml\XmlNode.cs
//
//////////////////////////////////////////////////////////////////////////////

using System;
using System.Globalization;
using System.Text;

namespace Microsoft.Singularity.Xml
{
    /// <summary>
    /// Summary description for XmlNode.
    /// </summary>
    public class XmlNode
    {
        internal struct XmlAttr
        {
            internal readonly string name;
            internal readonly string value;

            internal XmlAttr(string name, string value)
            {
                this.name = name != null ? name : "";
                this.value = value;
            }
        }

        private const int   defaultAttributesCapacity = 8;
        private const int   defaultChildrenCapacity = 8;

        private readonly string name;
        private string          text;
        private XmlAttr[]       attributes;
        private int             attributesUsed;
        private XmlNode[]       children;
        private int             childrenUsed;

        public XmlNode(string name)
        {
            this.name = name != null ? name : "";
            this.text = "";
        }

        public int CountNamedChildren(string name)
        {
            int result = 0;
            XmlNode[] childs = Children;
            foreach (XmlNode child in childs) {
                if (child.Name == name) {
                    result++;
                }
            }
            return result;
        }

        public XmlNode GetChild(String[] childNames)
        {
            XmlNode node = this;

            for (int i = 0; node != null && i < childNames.Length; i++) {
                node = node.GetChild(childNames[i]);
            }
            return node;
        }

        public XmlNode GetChild(String childName)
        {
            XmlNode[] childs = Children;
            foreach (XmlNode child in childs) {
                if (child.Name == childName) {
                    return child;
                }
            }
            return null;
        }

        public string Name
        {
            get { return name; }
        }

        public void AddChild(XmlNode node)
        {
            if (children == null) {
                children = new XmlNode[defaultChildrenCapacity];
            }
            else if (childrenUsed == children.Length) {
                XmlNode[] dest = new XmlNode[childrenUsed * 2];
                for (int i = 0; i < childrenUsed; i++) {
                    dest[i] = children[i];
                }
                children = dest;
            }
            children[childrenUsed++] = node;
        }

        public string  GetAttribute(string name)
        {
            return this[name];
        }
        
        public XmlNode CreateNode (string name)
        {
            return new XmlNode(name);
        }
        
        public XmlNode[] ChildNodes
        {
            get { return this.Children ;}
        }
        public XmlNode[] Children
        {
            get {
                if (children == null) {
                    children = new XmlNode[0];
                }
                else if (childrenUsed != children.Length) {
                    // optimize for the case that the XML tree is read once
                    // and then remains static.
                    XmlNode[] dest = new XmlNode[childrenUsed];
                    for (int i = 0; i < childrenUsed; i++) {
                        dest[i] = children[i];
                    }
                    children = dest;
                }
                return children;
            }
        }

        public void AddText(string text)
        {
            this.text += text;
        }

        public string Text
        {
            get { return text; }
        }

        public string this[string attributeName]
        {
            get
            {
                for (int i = 0; i < attributesUsed; i++) {
                    if (attributes[i].name == attributeName) {
                        return attributes[i].value;
                    }
                }
                return null;
            }
        }

        public void AddAttribute(string name, string value)
        {
            if (attributes == null) {
                attributes = new XmlAttr[defaultAttributesCapacity];
            }
            else if (attributesUsed == attributes.Length) {
                XmlAttr[] dest = new XmlAttr[attributesUsed * 2];
                for (int i = 0; i < attributesUsed; i++) {
                    dest[i] = attributes[i];
                }
                attributes = dest;
            }
            attributes[attributesUsed++] = new XmlAttr(name, value);
        }

        public void PrependAttribute(string name, string value)
        {
            if (attributes == null) {
                attributes = new XmlAttr[defaultAttributesCapacity];
            }
            else if (attributesUsed == attributes.Length) {
                XmlAttr[] dest = new XmlAttr[attributesUsed * 2];
                for (int i = 0; i < attributesUsed; i++) {
                    dest[i+1] = attributes[i];
                }
                attributes = dest;
            }
            else {
                //shift everything up one
                for (int i = attributesUsed; i > 0; i--) {
                    attributes[i] = attributes[i-1];
                }
            }
            attributes[0] = new XmlAttr(name, value);
            attributesUsed++;
        }

        //
        // Safe access to attributes:
        //      since the kernel is going to use this object, we should
        //      push the error-checking into the object instead of risking
        //      the kernel forgetting to error check in some obscure method
        //

        public string GetAttribute(string attributeName, string defaultValue)
        {
            string value = this[attributeName];
            if (value == null) {
                return defaultValue;
            }
            return value;
        }

        public bool GetAttribute(string attributeName, bool defaultValue)
        {
            string value = this[attributeName];
            if (value == null) {
                return defaultValue;
            }
            return (value == "true" || value == "True");
        }

        public int GetAttribute(string attributeName, int defaultValue)
        {
            string value = this[attributeName];
            if (value == null) {
                return defaultValue;
            }
            if (value.StartsWith("0x") || value.StartsWith("0X")) {
                return System.Int32.Parse(value, NumberStyles.AllowHexSpecifier);
            }
            else {
                return System.Int32.Parse(value);
            }
        }

        [CLSCompliant(false)]
        public UIntPtr GetAttributeAsUIntPtr(string attributeName, UIntPtr defaultValue)
        {
            string value = this[attributeName];
            if (value == null) {
                return defaultValue;
            }
            if (value.StartsWith("0x") || value.StartsWith("0X")) {
                return System.UIntPtr.Parse(value, NumberStyles.AllowHexSpecifier);
            }
            else {
                return System.UIntPtr.Parse(value);
            }
        }

        public bool HasAttribute(string attributeName)
        {
            return this[attributeName] != null;
        }

        public string GetAttributes()
        {
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < attributesUsed; i++) {
                sb.Append(" ");
                sb.Append(attributes[i].name);
                sb.Append("=\"");
                sb.Append(attributes[i].value);
                sb.Append("\"");
            }
            return sb.ToString();
        }
    }
}
