//---------------------------------------------------------------------
// <copyright file="AssociationType.cs" company="Microsoft">
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//
// @owner       [....]
// @backupOwner [....]
//---------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Text;
using System.Threading;
using System.Diagnostics;

namespace System.Data.Metadata.Edm
{
    /// <summary>
    /// Represents the EDM Association Type
    /// </summary>
    public sealed class AssociationType : RelationshipType
    {
        #region Constructors
        /// <summary>
        /// Initializes a new instance of Association Type with the given name, namespace, version and ends
        /// </summary>
        /// <param name="name">name of the association type</param>
        /// <param name="namespaceName">namespace of the association type</param>
        /// <param name="foreignKey">is this a foreign key (FK) relationship?</param>
        /// <param name="dataSpace">dataSpace in which this AssociationType belongs to</param>
        /// <exception cref="System.ArgumentNullException">Thrown if either the name, namespace or version attributes are null</exception>
        internal AssociationType(string name,
                                 string namespaceName,
                                 bool foreignKey,
                                 DataSpace dataSpace)
            : base(name, namespaceName, dataSpace)
        {
            _referentialConstraints = new ReadOnlyMetadataCollection<ReferentialConstraint>(new MetadataCollection<ReferentialConstraint>());
            _isForeignKey = foreignKey;
        }
        #endregion

        #region Fields
        private readonly ReadOnlyMetadataCollection<ReferentialConstraint> _referentialConstraints;
        private FilteredReadOnlyMetadataCollection<AssociationEndMember, EdmMember> _associationEndMembers;
        private readonly bool _isForeignKey;
        #endregion

        #region Properties
        /// <summary>
        /// Returns the kind of the type
        /// </summary>
        public override BuiltInTypeKind BuiltInTypeKind { get { return BuiltInTypeKind.AssociationType; } }

        /// <summary>
        /// Returns the list of ends for this association type
        /// </summary>
        public ReadOnlyMetadataCollection<AssociationEndMember> AssociationEndMembers
        {
            get
            {
                Debug.Assert(IsReadOnly, "this is a wrapper around this.Members, don't call it during metadata loading, only call it after the metadata is set to read-only");
                if (null == _associationEndMembers)
                {
                    Interlocked.CompareExchange(ref _associationEndMembers,
                        new FilteredReadOnlyMetadataCollection<AssociationEndMember, EdmMember>(
                            this.Members, Helper.IsAssociationEndMember), null);
                }
                return _associationEndMembers;
            }
        }

        /// <summary>
        /// Returns the list of constraints for this association type
        /// </summary>
        [MetadataProperty(BuiltInTypeKind.ReferentialConstraint, true)]
        public ReadOnlyMetadataCollection<ReferentialConstraint> ReferentialConstraints
        {
            get
            {
                return _referentialConstraints;
            }
        }

        /// <summary>
        /// Indicates whether this is a foreign key relationship.
        /// </summary>
        [MetadataProperty(PrimitiveTypeKind.Boolean, false)]
        public bool IsForeignKey
        {
            get
            {
                return _isForeignKey;
            }
        }
        #endregion

        #region Methods

        /// <summary>
        /// Validates a EdmMember object to determine if it can be added to this type's 
        /// Members collection. If this method returns without throwing, it is assumed
        /// the member is valid. 
        /// </summary>
        /// <param name="member">The member to validate</param>
        /// <exception cref="System.ArgumentException">Thrown if the member is not an AssociationEndMember</exception>
        internal override void ValidateMemberForAdd(EdmMember member)
        {
            Debug.Assert(
                (member is AssociationEndMember),
                "Only members of type AssociationEndMember may be added to Association definitions.");
        }

        /// <summary>
        /// Sets this item to be read-only, once this is set, the item will never be writable again.
        /// </summary>
        internal override void SetReadOnly()
        {
            if (!IsReadOnly)
            {
                base.SetReadOnly();
                this.ReferentialConstraints.Source.SetReadOnly();
            }
        }

        /// <summary>
        /// Add the given referential constraint to the collection of referential constraints
        /// </summary>
        /// <param name="referentialConstraint"></param>
        internal void AddReferentialConstraint(ReferentialConstraint referentialConstraint)
        {
            this.ReferentialConstraints.Source.Add(referentialConstraint);
        }
        #endregion
    }
}
