//---------------------------------------------------------------------
// 
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// 
//
// @owner       Microsoft
// @backupOwner Microsoft
//---------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Diagnostics;
using System.Reflection;
using System.Data.Common.Utils;
namespace System.Data.Metadata.Edm
{
    /// 
    /// Metadata collection class supporting delay-loading of system item attributes and
    /// extended attributes.
    /// 
    internal sealed class MetadataPropertyCollection : MetadataCollection
    {
        /// 
        /// Constructor taking item.
        /// 
        /// Item with which the collection is associated.
        internal MetadataPropertyCollection(MetadataItem item)
            : base(GetSystemMetadataProperties(item))
        {
        }
        private readonly static Memoizer s_itemTypeMemoizer =
            new Memoizer(clrType => new ItemTypeInformation(clrType), null);
        // Given an item, returns all system type attributes for the item.
        private static IEnumerable GetSystemMetadataProperties(MetadataItem item)
        {
            EntityUtil.CheckArgumentNull(item, "item");
            Type type = item.GetType();
            ItemTypeInformation itemTypeInformation = GetItemTypeInformation(type);
            return itemTypeInformation.GetItemAttributes(item);
        }
        // Retrieves metadata for type.
        private static ItemTypeInformation GetItemTypeInformation(Type clrType)
        {
            return s_itemTypeMemoizer.Evaluate(clrType);
        }
        /// 
        /// Encapsulates information about system item attributes for a particular item type.
        /// 
        private class ItemTypeInformation
        {
            /// 
            /// Retrieves system attribute information for the given type.
            /// Requires: type must derive from MetadataItem
            /// 
            /// Type
            internal ItemTypeInformation(Type clrType)
            {
                Debug.Assert(null != clrType);
                _itemProperties = GetItemProperties(clrType);
            }
            private readonly List _itemProperties;
            // Returns system item attributes for the given item.
            internal IEnumerable GetItemAttributes(MetadataItem item)
            {
                foreach (ItemPropertyInfo propertyInfo in _itemProperties)
                {
                    yield return propertyInfo.GetMetadataProperty(item);
                }
            }
            // Gets type information for item with the given type. Uses cached information where 
            // available.
            private static List GetItemProperties(Type clrType)
            {
                List result = new List();
                foreach (PropertyInfo propertyInfo in clrType.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
                {
                    foreach (MetadataPropertyAttribute attribute in propertyInfo.GetCustomAttributes(
                        typeof(MetadataPropertyAttribute), false))
                    {
                        result.Add(new ItemPropertyInfo(propertyInfo, attribute));
                    }
                }
                return result;
            }
        }
        /// 
        /// Encapsulates information about a CLR property of an item class.
        /// 
        private class ItemPropertyInfo
        {
            /// 
            /// Initialize information.
            /// Requires: attribute must belong to the given property.
            /// 
            /// Property referenced.
            /// Attribute for the property.
            internal ItemPropertyInfo(PropertyInfo propertyInfo, MetadataPropertyAttribute attribute)
            {
                Debug.Assert(null != propertyInfo);
                Debug.Assert(null != attribute);
                _propertyInfo = propertyInfo;
                _attribute = attribute;
            }
            private readonly MetadataPropertyAttribute _attribute;
            private readonly PropertyInfo _propertyInfo;
            /// 
            /// Given an item, returns an instance of the item attribute described by this class.
            /// 
            /// Item from which to retrieve attribute.
            /// Item attribute.
            internal MetadataProperty GetMetadataProperty(MetadataItem item)
            {
                return new MetadataProperty(_propertyInfo.Name, _attribute.Type, _attribute.IsCollectionType,
                    new MetadataPropertyValue(_propertyInfo, item));
            }
        }
    }
}