//---------------------------------------------------------------------
// <copyright file="ObjectTypeMapping.cs" company="Microsoft">
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//
// @owner       [....]
// @backupOwner [....]
//---------------------------------------------------------------------

namespace System.Data.Mapping {

    using System;
    using System.Collections.Generic;
    using System.Data.Metadata.Edm;
    using System.Diagnostics;

    /// <summary>
    /// Represents the metadata for OCObjectMapping.
    /// </summary>
    internal class ObjectTypeMapping : Map {
        #region Constructors
        /// <summary>
        /// Construct a new ObjectTypeMapping object
        /// </summary>
        /// <param name="clrType"></param>
        /// <param name="cdmType"></param>
        internal ObjectTypeMapping(EdmType clrType, EdmType cdmType) {
            Debug.Assert(clrType.BuiltInTypeKind == cdmType.BuiltInTypeKind, "BuiltInTypeKind must be the same for both types");
            this.m_clrType = clrType;
            this.m_cdmType = cdmType;
            identity = clrType.Identity + ObjectMslConstructs.IdentitySeperator + cdmType.Identity;

            if (Helper.IsStructuralType(cdmType))
            {
                m_memberMapping = new Dictionary<string, ObjectMemberMapping>(((StructuralType)cdmType).Members.Count);
            }
            else
            {
                m_memberMapping = EmptyMemberMapping;
            }
        }
        #endregion

        #region Fields
        #region Internal
        private readonly EdmType m_clrType;  //type on the Clr side that is being mapped
        private readonly EdmType m_cdmType;  //type on the Cdm side that is being mapped
        private readonly string identity;
        private readonly Dictionary<string, ObjectMemberMapping> m_memberMapping; //Indexes into the member mappings collection based on clr member name

        private static readonly Dictionary<string, ObjectMemberMapping> EmptyMemberMapping
            = new Dictionary<string, ObjectMemberMapping>(0);
        #endregion
        #endregion

        #region Properties
        /// <summary>
        /// Gets the type kind for this item
        /// </summary>
        public override BuiltInTypeKind BuiltInTypeKind {
            get { return BuiltInTypeKind.MetadataItem; }
        }

        /// <summary>
        /// The reference to the Clr type in Metadata
        /// that participates in this mapping instance
        /// </summary>
        internal EdmType ClrType {
            get {
                return this.m_clrType;
            }
        }

        /// <summary>
        /// The reference to the Cdm type in Metadata
        /// that participates in this mapping instance
        /// </summary>
        internal override MetadataItem EdmItem {
            get {
                return this.EdmType;
            }
        }

        /// <summary>
        /// The reference to the Cdm type in Metadata
        /// that participates in this mapping instance
        /// </summary>
        internal EdmType EdmType {
            get {
                return this.m_cdmType;
            }
        }

        /// <summary>
        /// Returns the Identity of ObjectTypeMapping.
        /// The identity for an Object Type Map is the concatenation of
        /// CLR Type Idntity + ':' + CDM Type Identity
        /// </summary>
        internal override string Identity {
            get {
                return identity;
            }
        }

        #endregion

        #region Methods
        /// <summary>
        /// get a MemberMap for the member name specified
        /// </summary>
        /// <param name="cdmPropertyName">the name of the CDM member for which map needs to be retrieved</param>
        internal ObjectPropertyMapping GetPropertyMap(String propertyName)
        {
            ObjectMemberMapping memberMapping = GetMemberMap(propertyName, false /*ignoreCase*/);

            if (memberMapping != null &&
                memberMapping.MemberMappingKind == MemberMappingKind.ScalarPropertyMapping ||
                memberMapping.MemberMappingKind == MemberMappingKind.ComplexPropertyMapping)
            {
                return (ObjectPropertyMapping)memberMapping;
            }

            return null;
        }

        /// <summary>
        /// Add a member mapping as a child of this object mapping
        /// </summary>
        /// <param name="property">child property mapping to be added</param>
        internal void AddMemberMap(ObjectMemberMapping memberMapping) {
            Debug.Assert(memberMapping.ClrMember.Name == memberMapping.EdmMember.Name,
                "Both clrmember and edmMember name must be the same");
            //Check to see if either the Clr member or the Cdm member specified in this 
            //type has already been mapped.
            Debug.Assert(!m_memberMapping.ContainsKey(memberMapping.EdmMember.Name));
            Debug.Assert(!Type.ReferenceEquals(m_memberMapping, EmptyMemberMapping),
                "Make sure you don't add anything to the static emtpy member mapping");
            m_memberMapping.Add(memberMapping.EdmMember.Name, memberMapping);
        }

        /// <summary>
        /// Returns the member map for the given clr member
        /// </summary>
        /// <param name="clrPropertyName"></param>
        /// <param name="ignoreCase"></param>
        /// <returns></returns>
        internal ObjectMemberMapping GetMemberMapForClrMember(string clrMemberName, bool ignoreCase)
        {
            return GetMemberMap(clrMemberName, ignoreCase);
        }

        /// <summary>
        /// returns the member mapping for the given member
        /// </summary>
        /// <param name="propertyName"></param>
        /// <param name="ignoreCase"></param>
        /// <returns></returns>
        private ObjectMemberMapping GetMemberMap(string propertyName, bool ignoreCase)
        {
            EntityUtil.CheckStringArgument(propertyName, "propertyName");
            ObjectMemberMapping memberMapping = null;

            if (!ignoreCase)
            {
                //First get the index of the member map from the clr indexs
                m_memberMapping.TryGetValue(propertyName, out memberMapping);
            }
            else
            {
                foreach (KeyValuePair<string, ObjectMemberMapping> keyValuePair in m_memberMapping)
                {
                    if (keyValuePair.Key.Equals(propertyName, StringComparison.OrdinalIgnoreCase))
                    {
                        if (memberMapping != null)
                        {
                            throw new MappingException(System.Data.Entity.Strings.Mapping_Duplicate_PropertyMap_CaseInsensitive(
                                propertyName));
                        }
                        memberMapping = keyValuePair.Value;
                    }
                }
            }

            return memberMapping;
        }

        /// <summary>
        /// Overriding System.Object.ToString to provide better String representation 
        /// for this type.
        /// </summary>
        public override string ToString()
        {
            return this.Identity;
        }
        #endregion
    }
}
