// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.

namespace Microsoft.Data.Entity.Design.Model
{
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Diagnostics.CodeAnalysis;
    using System.Xml.Linq;
    using Microsoft.Data.Entity.Design.Model.Designer;
    using Microsoft.Data.Entity.Design.VersioningFacade;

    internal class EFDesignerInfoRoot : EFElement
    {
        internal static readonly string ElementName = "Designer";
        protected Dictionary<string, DesignerInfo> _designerInfos = new Dictionary<string, DesignerInfo>();

        private Diagrams _diagrams;
        private readonly DiagramArtifact _diagramArtifact;

        [SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
        internal EFDesignerInfoRoot(EFArtifact parent, XElement element)
            : base(parent, element)
        {
            // Allow the Diagrams node comes from DiagramArtifact.
            var artifact = Artifact as EntityDesignArtifact;
            if (artifact != null
                && artifact.DiagramArtifact != null)
            {
                _diagramArtifact = artifact.DiagramArtifact;
            }
        }

        internal bool TryGetDesignerInfo(string infoName, out DesignerInfo designerInfo)
        {
            return _designerInfos.TryGetValue(infoName, out designerInfo);
        }

        internal void AddDesignerInfo(string infoName, DesignerInfo info)
        {
            _designerInfos.Add(infoName, info);
        }

        internal XNamespace XNamespace
        {
            get
            {
                if (XElement != null
                    && XElement.Name != null
                    && XElement.Name.Namespace != null)
                {
                    return XElement.Name.Namespace;
                }
                else
                {
                    // if for some reason the XElement backing this model node doesn't exist yet, we'll use the 
                    // desired namespace for the EF version of the artifact. 
                    var schemaVersion = SchemaManager.GetSchemaVersion(Artifact.XDocument.Root.Name.Namespace);
                    return SchemaManager.GetEDMXNamespaceName(schemaVersion);
                }
            }
        }

        internal Diagrams Diagrams
        {
            get
            {
                if (_diagramArtifact != null)
                {
                    return _diagramArtifact.Diagrams;
                }
                return _diagrams;
            }
        }

        internal static XNamespace GetRootNamespace(EFObject node)
        {
            var currNode = node;
            EFDesignerInfoRoot model = null;
            XNamespace ns = null;

            while (currNode != null)
            {
                model = currNode as EFDesignerInfoRoot;
                if (model != null)
                {
                    ns = model.XNamespace;
                    break;
                }
                else
                {
                    currNode = currNode.Parent;
                }
            }

            return ns;
        }

        #region overrides

        // we unfortunately get a warning from the compiler when we use the "base" keyword in "iterator" types generated by using the
        // "yield return" keyword.  By adding this method, I was able to get around this.  Unfortunately, I wasn't able to figure out
        // a way to implement this once and have derived classes share the implementation (since the "base" keyword is resolved at 
        // compile-time and not at runtime.
        private IEnumerable<EFObject> BaseChildren
        {
            get { return base.Children; }
        }

        internal override IEnumerable<EFObject> Children
        {
            get
            {
                foreach (var efobj in BaseChildren)
                {
                    yield return efobj;
                }

                foreach (var info in _designerInfos.Values)
                {
                    yield return info;
                }

                yield return _diagrams;
            }
        }

        protected override void OnChildDeleted(EFContainer efContainer)
        {
            var child2 = efContainer as DesignerInfo;
            if (child2 != null)
            {
                var infoEnum = _designerInfos.GetEnumerator();
                while (infoEnum.MoveNext())
                {
                    if (infoEnum.Current.Value == child2)
                    {
                        _designerInfos.Remove(infoEnum.Current.Key);
                        break;
                    }
                }
                return;
            }

            if (efContainer.Equals(_diagrams))
            {
                _diagrams = null;
            }

            base.OnChildDeleted(efContainer);
        }

#if DEBUG
        internal override ICollection<string> MyAttributeNames()
        {
            var s = base.MyAttributeNames();
            return s;
        }
#endif

#if DEBUG
        internal override ICollection<string> MyChildElementNames()
        {
            var s = base.MyChildElementNames();
            s.Add(ConnectionDesignerInfo.ElementName);
            s.Add(Diagrams.ElementName);
            return s;
        }
#endif

        protected override void PreParse()
        {
            Debug.Assert(State != EFElementState.Parsed, "this object should not already be in the parsed state");

            if (_designerInfos != null)
            {
                if (_designerInfos.Values.Count > 0)
                {
                    foreach (var info in _designerInfos.Values)
                    {
                        info.Dispose();
                    }
                }
                _designerInfos.Clear();
            }

            ClearEFObject(_diagrams);
            _diagrams = null;

            base.PreParse();
        }

        [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")]
        internal override bool ParseSingleElement(ICollection<XName> unprocessedElements, XElement element)
        {
            if (element.Name.LocalName == "Connection")
            {
                DesignerInfo connectionDesignerInfo = new ConnectionDesignerInfo(this, element);
                connectionDesignerInfo.Parse(unprocessedElements);
                _designerInfos.Add(element.Name.LocalName, connectionDesignerInfo);
            }
            else if (element.Name.LocalName == OptionsDesignerInfo.ElementName)
            {
                DesignerInfo optionsDesignerInfo = new OptionsDesignerInfo(this, element);
                optionsDesignerInfo.Parse(unprocessedElements);
                _designerInfos.Add(element.Name.LocalName, optionsDesignerInfo);
            }
            else if (_diagramArtifact == null
                     && element.Name.LocalName == Diagrams.ElementName) // only parse Diagrams element if DiagramArtifact is not available.
            {
                _diagrams = new Diagrams(this, element);
                _diagrams.Parse(unprocessedElements);
            }
            else
            {
                return base.ParseSingleElement(unprocessedElements, element);
            }
            return true;
        }

        #endregion
    }
}
