// 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.Entity
{
    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Diagnostics.CodeAnalysis;
    using System.Text;
    using System.Xml.Linq;
    using Microsoft.Data.Entity.Design.Model.Commands;

    internal abstract class PropertyRefContainer : EFElement
    {
        protected List<PropertyRef> _propertyRefs = new List<PropertyRef>();

        internal PropertyRefContainer(EFContainer parent, XElement element)
            : base(parent, element)
        {
        }

        [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")]
        internal void AddPropertyRef(Property property)
        {
            Debug.Assert(property != null, "property should not be null");
            if (property != null)
            {
                var propRef = new PropertyRef(this, null, GetNameNormalizerForPropertyRef());
                propRef.Name.SetRefName(property);
                propRef.Normalize();
                propRef.Resolve(Artifact.ModelManager.GetArtifactSet(Artifact.Uri));
                _propertyRefs.Add(propRef);
            }
        }

        internal void RemovePropertyRef(Property property)
        {
            Debug.Assert(property != null, "property should not be null");
            if (property != null)
            {
                var propRef = GetPropertyRef(property);
                if (propRef != null)
                {
                    // TODO: Overload Delete on PropertyRef to Remove itself from Container
                    //_propertyRefs.Remove(propRef);
                    propRef.Delete();
                }
            }
        }

        internal PropertyRef GetPropertyRef(Property property)
        {
            foreach (var propRef in _propertyRefs)
            {
                if (propRef.Name.Status == BindingStatus.Known
                    && propRef.Name.Target == property)
                {
                    return propRef;
                }
            }

            return null;
        }

        // 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 pr in _propertyRefs)
                {
                    yield return pr;
                }
            }
        }

        internal ICollection<PropertyRef> PropertyRefs
        {
            get { return _propertyRefs.AsReadOnly(); }
        }

        internal IEnumerable<Property> Properties
        {
            get
            {
                foreach (var pr in PropertyRefs)
                {
                    if (pr.Name.Target != null)
                    {
                        yield return pr.Name.Target;
                    }
                }
            }
        }

        protected override void OnChildDeleted(EFContainer efContainer)
        {
            var child1 = efContainer as PropertyRef;
            if (child1 != null)
            {
                _propertyRefs.Remove(child1);
                return;
            }

            base.OnChildDeleted(efContainer);
        }

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

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

            ClearEFObjectCollection(_propertyRefs);
            base.PreParse();
        }

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

        internal string GetPropertyRefsAsString()
        {
            if (_propertyRefs.Count == 0)
            {
                return String.Empty;
            }
            else
            {
                var sb = new StringBuilder();

                foreach (var pr in _propertyRefs)
                {
                    sb.Append(pr.Name.RefName + ", ");
                }

                // remove last comma
                sb = sb.Remove(sb.Length - 2, 2);
                return sb.ToString();
            }
        }

        internal abstract SingleItemBinding<Property>.NameNormalizer GetNameNormalizerForPropertyRef();

        internal virtual DeleteEFElementCommand GetDeleteCommandForChild(PropertyRef pref)
        {
            return pref.GetDeleteCommand();
        }
    }
}
