// 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.Designer
{
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Diagnostics.CodeAnalysis;
    using System.Xml.Linq;
    using Microsoft.Data.Tools.Model.Diagram;

    internal abstract class Connector : BaseDiagramObject
    {
        internal static readonly string AttributeManuallyRouted = "ManuallyRouted";

        private DefaultableValue<bool> _manuallyRoutedAttr;
        private readonly List<ConnectorPoint> _connectorPoints = new List<ConnectorPoint>();

        protected Connector(EFElement parent, XElement element)
            : base(parent, element)
        {
        }

        internal DefaultableValue<bool> ManuallyRouted
        {
            get
            {
                if (_manuallyRoutedAttr == null)
                {
                    _manuallyRoutedAttr = new ManuallyRoutedDefaultableValue(this);
                }
                return _manuallyRoutedAttr;
            }
        }

        internal ICollection<ConnectorPoint> ConnectorPoints
        {
            get { return _connectorPoints.AsReadOnly(); }
        }

        internal void AddConnectorPoint(ConnectorPoint point)
        {
            _connectorPoints.Add(point);
        }

        #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;
                }

                yield return ManuallyRouted;

                foreach (EFObject efobj in _connectorPoints)
                {
                    yield return efobj;
                }
            }
        }

        protected override void OnChildDeleted(EFContainer efContainer)
        {
            var point = efContainer as ConnectorPoint;
            if (point != null)
            {
                _connectorPoints.Remove(point);
            }

            base.OnChildDeleted(efContainer);
        }

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

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

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

            ClearEFObject(_manuallyRoutedAttr);
            _manuallyRoutedAttr = null;
            ClearEFObjectCollection(_connectorPoints);

            base.PreParse();
        }

        [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")]
        internal override bool ParseSingleElement(ICollection<XName> unprocessedElements, XElement elem)
        {
            if (elem.Name.LocalName == ConnectorPoint.ElementName)
            {
                var point = new ConnectorPoint(this, elem);
                point.Parse(unprocessedElements);
                AddConnectorPoint(point);
            }
            else
            {
                return base.ParseSingleElement(unprocessedElements, elem);
            }
            return true;
        }

        #endregion

        private class ManuallyRoutedDefaultableValue : DefaultableValue<bool>
        {
            internal ManuallyRoutedDefaultableValue(EFElement parent)
                : base(parent, AttributeManuallyRouted)
            {
            }

            internal override string AttributeName
            {
                get { return AttributeManuallyRouted; }
            }

            public override bool DefaultValue
            {
                get { return false; }
            }
        }
    }
}
