//---------------------------------------------------------------------
// 
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// 
//
// @owner       Microsoft
// @backupOwner Microsoft
//---------------------------------------------------------------------
namespace System.Data.EntityModel.SchemaObjectModel
{
    using System;
    using System.Collections.Generic;
    using System.Data;
    using System.Data.Entity;
    using System.Data.Metadata.Edm;
    using System.Diagnostics;
    using System.Globalization;
    using System.IO;
    using System.Xml;
    using System.Xml.Linq;
    /// 
    /// Summary description for SchemaElement.
    /// 
    [DebuggerDisplay("Name={Name}")]
    internal abstract class SchemaElement
    {
        // see http://www.w3.org/TR/2006/REC-xml-names-20060816/
        internal const string XmlNamespaceNamespace = "http://www.w3.org/2000/xmlns/";
        #region Instance Fields
        private SchemaElement _parentElement = null;
        private Schema _schema = null;
        private int _lineNumber = 0;
        private int _linePosition = 0;
        private string _name = null;
        private DocumentationElement _documentation = null;
        private List _otherContent;
        #endregion
        #region Static Fields
        /// 
        protected const int MaxValueVersionComponent = short.MaxValue;
        #endregion
        #region Public Properties
        /// 
        /// 
        /// 
        internal  int LineNumber
        {
            get
            {
                return _lineNumber;
            }
        }
        /// 
        /// 
        /// 
        internal  int LinePosition
        {
            get
            {
                return _linePosition;
            }
        }
        /// 
        /// 
        /// 
        public virtual string Name
        {
            get
            {
                return _name;
            }
            set
            {
                _name = value;
            }
        }
        /// 
        /// 
        /// 
        internal  DocumentationElement Documentation
        {
            get
            {
                return _documentation;
            }
            set
            {
                _documentation = value;
            }
        }
        /// 
        /// 
        /// 
        internal  SchemaElement ParentElement
        {
            get
            {
                return _parentElement;
            }
            private set
            {
                _parentElement = value;
            }
        }
        /// 
        /// 
        /// 
        internal Schema Schema
        {
            get
            {
                return _schema;
            }
            set
            {
                _schema = value;
            }
        }
        
        /// 
        /// 
        /// 
        public  virtual string FQName
        {
            get
            {
                return Name;
            }
        }
        /// 
        /// 
        /// 
        public virtual string Identity
        {
            get
            {
                return Name;
            }
        }
       
        public List OtherContent
        {
            get 
            {
                if (_otherContent == null)
                {
                    _otherContent = new List();
                }
                return _otherContent; 
            }
        }
        #endregion
        #region Internal Methods
        /// 
        /// Validates this element and its children
        /// 
        
        internal virtual void Validate()
        {
        }
        /// 
        /// 
        /// 
        /// 
        /// 
        /// 
        /// 
        /// 
        internal void AddError( ErrorCode errorCode, EdmSchemaErrorSeverity severity, int lineNumber, int linePosition, object message )
        {
            AddError(errorCode,severity,SchemaLocation,lineNumber,linePosition,message);
        }
        /// 
        /// 
        /// 
        /// 
        /// 
        /// 
        /// 
        internal void AddError( ErrorCode errorCode, EdmSchemaErrorSeverity severity, XmlReader reader, object message )
        {
            int lineNumber;
            int linePosition;
            GetPositionInfo(reader, out lineNumber, out linePosition);
            AddError(errorCode,severity,SchemaLocation,lineNumber,linePosition,message);
        }
        /// 
        /// 
        /// 
        /// 
        /// 
        /// 
        internal void AddError( ErrorCode errorCode, EdmSchemaErrorSeverity severity, object message )
        {
            AddError(errorCode,severity,SchemaLocation,LineNumber,LinePosition,message);
        }
        /// 
        /// 
        /// 
        /// 
        /// 
        /// 
        /// 
        internal void AddError( ErrorCode errorCode, EdmSchemaErrorSeverity severity, SchemaElement element, object message )
        {
            AddError(errorCode,severity,element.Schema.Location,element.LineNumber,element.LinePosition,message);
        }
        /// 
        /// 
        /// 
        /// 
        /// 
        internal void Parse(XmlReader reader)
        {
            GetPositionInfo(reader);
            bool hasEndElement = !reader.IsEmptyElement;
            Debug.Assert(reader.NodeType == XmlNodeType.Element);
            for ( bool more = reader.MoveToFirstAttribute(); more; more = reader.MoveToNextAttribute() )
            {
                ParseAttribute(reader);
            }
            HandleAttributesComplete();
            bool done = !hasEndElement;
            bool skipToNextElement = false;
            while ( !done )
            {
                if ( skipToNextElement )
                {
                    skipToNextElement = false;
                    reader.Skip();
                    if ( reader.EOF )
                        break;
                }
                else
                {
                    if ( !reader.Read() )
                        break;
                }
                switch ( reader.NodeType )
                {
                    case XmlNodeType.Element:
                        skipToNextElement = ParseElement(reader);
                        break;
                    case XmlNodeType.EndElement:
                    {
                        done = true;
                        break;
                    }
                    case XmlNodeType.CDATA:
                    case XmlNodeType.Text:
                    case XmlNodeType.SignificantWhitespace:
                        ParseText(reader);
                        break;
                        // we ignore these childless elements
                    case XmlNodeType.Whitespace:
                    case XmlNodeType.XmlDeclaration:
                    case XmlNodeType.Comment:
                    case XmlNodeType.Notation:
                    case XmlNodeType.ProcessingInstruction:
                    {
                        break;
                    }
                        // we ignore these elements that can have children
                    case XmlNodeType.DocumentType:
                    case XmlNodeType.EntityReference:
                    {
                        skipToNextElement = true;
                        break;
                    }
                    default:
                    {
                        AddError( ErrorCode.UnexpectedXmlNodeType, EdmSchemaErrorSeverity.Error, reader,
                            System.Data.Entity.Strings.UnexpectedXmlNodeType(reader.NodeType));
                        skipToNextElement = true;
                        break;
                    }
                }
            }
            HandleChildElementsComplete();
            if ( reader.EOF && reader.Depth > 0 )
            {
                AddError( ErrorCode.MalformedXml, EdmSchemaErrorSeverity.Error, 0, 0,
                    System.Data.Entity.Strings.MalformedXml(LineNumber,LinePosition));
            }
        }
        /// 
        /// Set the current line number and position for an XmlReader
        /// 
        /// the reader whose position is desired
        internal void GetPositionInfo(XmlReader reader)
        {
            GetPositionInfo(reader,out _lineNumber,out _linePosition);
        }
        /// 
        /// Get the current line number and position for an XmlReader
        /// 
        /// the reader whose position is desired
        /// the line number
        /// the line position
        internal static void GetPositionInfo(XmlReader reader, out int lineNumber, out int linePosition)
        {
            IXmlLineInfo xmlLineInfo = reader as IXmlLineInfo;
            if ( xmlLineInfo != null && xmlLineInfo.HasLineInfo() )
            {
                lineNumber = xmlLineInfo.LineNumber;
                linePosition = xmlLineInfo.LinePosition;
            }
            else
            {
                lineNumber = 0;
                linePosition = 0;
            }
        }
        
        /// 
        /// 
        /// 
        internal virtual void ResolveTopLevelNames()
        {
        }
        internal virtual void ResolveSecondLevelNames()
        {
        }
        #endregion
        #region Protected Methods
        /// 
        /// 
        /// 
        /// 
        internal SchemaElement(SchemaElement parentElement)
        {
            if ( parentElement != null )
            {
                ParentElement = parentElement;
                for ( SchemaElement element = parentElement; element != null; element = element.ParentElement )
                {
                    Schema schema = element as Schema;
                    if ( schema != null )
                    {
                        Schema = schema;
                        break;
                    }
                }
                
                if (Schema == null)
                {
                    throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.AllElementsMustBeInSchema);
                }
            }
        }
        internal SchemaElement(SchemaElement parentElement, string name)
            : this(parentElement)
        {
            _name = name;
        }
        /// 
        /// 
        /// 
        protected virtual void HandleAttributesComplete()
        {
        }
        /// 
        /// 
        /// 
        protected virtual void HandleChildElementsComplete()
        {
        }
        /// 
        /// 
        /// 
        /// 
        /// 
        /// 
        protected string HandleUndottedNameAttribute(XmlReader reader, string field)
        {
            string name = field;
            Debug.Assert(string.IsNullOrEmpty(field), string.Format(CultureInfo.CurrentCulture, "{0} is already defined", reader.Name));
            bool success = Utils.GetUndottedName(Schema, reader, out name);
            if ( !success )
                return name;
            return name;
        }
        /// 
        /// 
        /// 
        /// 
        /// 
        /// 
        /// 
        protected ReturnValue HandleDottedNameAttribute(XmlReader reader, string field, Func