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

    internal abstract class FunctionImportTypeMapping : EFElement
    {
        internal static readonly string AttributeTypeName = "TypeName";

        private SingleItemBinding<EFNormalizableItem> _typeName;

        private readonly List<FunctionImportScalarProperty> _scalarProperties = new List<FunctionImportScalarProperty>();

        internal FunctionImportTypeMapping(ResultMapping parent, XElement element)
            : base(parent, element)
        {
        }

        /// <summary>
        ///     Manages the content of the TypeName attribute
        /// </summary>
        internal SingleItemBinding<EFNormalizableItem> TypeName
        {
            get
            {
                if (_typeName == null)
                {
                    _typeName = new SingleItemBinding<EFNormalizableItem>(
                        this, AttributeTypeName, EFNormalizableItemDefaults.DefaultNameNormalizerForMSL);
                }
                return _typeName;
            }
        }

        internal void AddScalarProperty(FunctionImportScalarProperty scalarProperty)
        {
            _scalarProperties.Add(scalarProperty);
        }

        internal IList<FunctionImportScalarProperty> ScalarProperties()
        {
            return _scalarProperties.AsReadOnly();
        }

        /// <summary>
        ///     Returns first FunctionImportScalarProperty that Name attribute binds to specified Property
        /// </summary>
        /// <param name="property"></param>
        /// <returns></returns>
        internal FunctionImportScalarProperty FindScalarProperty(Property property)
        {
            foreach (var scalarProperty in _scalarProperties)
            {
                if (scalarProperty.Name.Target == property)
                {
                    return scalarProperty;
                }
            }
            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 child in ScalarProperties())
                {
                    yield return child;
                }

                yield return TypeName;
            }
        }

        protected override void OnChildDeleted(EFContainer efContainer)
        {
            var sp = efContainer as FunctionImportScalarProperty;
            if (sp != null)
            {
                _scalarProperties.Remove(sp);
                return;
            }

            base.OnChildDeleted(efContainer);
        }

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

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

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

            ClearEFObject(_typeName);
            _typeName = null;

            ClearEFObjectCollection(_scalarProperties);

            base.PreParse();
        }

        internal override bool ParseSingleElement(ICollection<XName> unprocessedElements, XElement elem)
        {
            if (elem.Name.LocalName == FunctionImportScalarProperty.ElementName)
            {
                var prop = new FunctionImportScalarProperty(this, elem);
                _scalarProperties.Add(prop);
                prop.Parse(unprocessedElements);
            }
            else
            {
                return base.ParseSingleElement(unprocessedElements, elem);
            }
            return true;
        }

        protected override void DoResolve(EFArtifactSet artifactSet)
        {
            TypeName.Rebind();
            if (TypeName.Status == BindingStatus.Known)
            {
                State = EFElementState.Resolved;
            }
        }
    }
}
