// 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 class PropertyMappingBase : EFElement
    {
        internal static readonly string AttributeName = "Name";
        internal static readonly string AttributeColumnName = "ColumnName";

        private SingleItemBinding<Property> _property;
        private SingleItemBinding<Property> _column;

        internal PropertyMappingBase(EFElement parent, XElement element)
            : base(parent, element)
        {
        }

        /// <summary>
        ///     This will return NULL if this is not in a fragment
        /// </summary>
        internal MappingFragment MappingFragment
        {
            get
            {
                // this Property may be inside of a ComplexProperty so we need to walk up the tree
                return GetParentOfType(typeof(MappingFragment)) as MappingFragment;
            }
        }

        /// <summary>
        ///     Manages the content of the Name attribute
        /// </summary>
        internal SingleItemBinding<Property> Name
        {
            get
            {
                if (_property == null)
                {
                    _property = new SingleItemBinding<Property>(
                        this,
                        AttributeName,
                        ProperyMappingNameNormalizer.NameNormalizer
                        );
                }
                return _property;
            }
        }

        /// <summary>
        ///     Manages the content of the ColumnName attribute
        /// </summary>
        internal SingleItemBinding<Property> ColumnName
        {
            get
            {
                if (_column == null)
                {
                    _column = new SingleItemBinding<Property>(
                        this,
                        AttributeColumnName,
                        PropertyMappingColumnNameNormalizer.NameNormalizer
                        );
                }
                return _column;
            }
        }

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

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

            ClearEFObject(_property);
            _property = null;

            ClearEFObject(_column);
            _column = null;

            base.PreParse();
        }

        internal override bool ParseSingleElement(ICollection<XName> unprocessedElements, XElement elem)
        {
            return false;
        }

        protected override void DoResolve(EFArtifactSet artifactSet)
        {
            // recompile the bindings at this point, but don't change the state of the item yet,
            // the derived classes do this because they have different rules
            Name.Rebind();
            ColumnName.Rebind();
        }

        // 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 Name;
                yield return ColumnName;
            }
        }

        internal ConceptualEntityType FirstBoundConceptualEntityType
        {
            get
            {
                var frag = MappingFragment;
                if (frag == null)
                {
                    // this is not a child of a MappingFragment, must be in an AssocSetMapping
                    return null;
                }

                var etm = frag.Parent as EntityTypeMapping;
                Debug.Assert(etm != null, "frag.Parent should be an EntityTypeMapping");

                return etm.FirstBoundConceptualEntityType;
            }
        }

        internal EntityType BoundStorageEntityType
        {
            get
            {
                var frag = MappingFragment;
                if (frag == null)
                {
                    // this is not a child of a MappingFragment, must be in an AssocSetMapping
                    return null;
                }

                var ses = frag.StoreEntitySet.Target as StorageEntitySet;
                if (ses != null)
                {
                    return ses.EntityType.Target;
                }

                return null;
            }
        }
    }
}
