//---------------------------------------------------------------------
// 
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// 
//
// @owner [....]
// @backupOwner [....]
//---------------------------------------------------------------------
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using System.Linq;
namespace System.Data.Common.Utils {
    // An interface for a set abstraction
    internal class Set : InternalBase, IEnumerable
    {
        #region Fields
        /// 
        /// Instance of set value comparer.
        /// 
        internal static readonly IEqualityComparer> ValueComparer = 
            new SetValueComparer();
        /// 
        /// Instance of empty set with default comparer.
        /// 
        internal static readonly Set Empty = new Set().MakeReadOnly();
        private readonly HashSet _values;
        private bool _isReadOnly;
        #endregion
        #region Constructors
        /// 
        /// Initialize set with the same values and comparer as other set.
        /// 
        internal Set(Set other)
            : this(other._values, other.Comparer)
        {
        }
        /// 
        /// Initialize empty set with default comparer.
        /// 
        internal Set()
            : this(null, null)
        {
        }
        /// 
        /// Initialize a set with the given elements and using default comparer.
        /// 
        internal Set(IEnumerable elements)
            : this(elements, null)
        {
        }
        /// 
        /// Initializes an empty set with the given comparer.
        /// 
        internal Set(IEqualityComparer comparer)
            : this(null, comparer)
        {
        }
        /// 
        /// Initialize a set with the given elements and comparer.
        /// 
        internal Set(IEnumerable elements, IEqualityComparer comparer)
        {
            _values = new HashSet(
                elements ?? Enumerable.Empty(),
                comparer ?? EqualityComparer.Default);
        }
        #endregion
        #region Properties
        /// 
        /// Gets the number of elements in this set.
        /// 
        internal int Count 
        { 
            get 
            { 
                return _values.Count; 
            } 
        }
        /// 
        /// Gets the comparer used to determine equality and hash codes for elements of the set.
        /// 
        internal IEqualityComparer Comparer
        {
            get
            {
                return _values.Comparer;
            }
        }
        #endregion
        #region Methods
        /// 
        /// Determines whether the given element exists in the set.
        /// 
        internal bool Contains(TElement element) 
        { 
            return _values.Contains(element); 
        }
        /// 
        /// Requires: !IsReadOnly
        /// Adds given element to the set. If the set already contains
        /// the element, does nothing.
        /// 
        internal void Add(TElement element) 
        {
            AssertReadWrite();
            _values.Add(element); 
        }
        /// 
        /// Requires: !IsReadOnly
        /// Adds given elements to the set. If the set already contains
        /// one of the elements, does nothing.
        /// 
        internal void AddRange(IEnumerable elements)
        {
            AssertReadWrite();
            foreach (TElement element in elements)
            {
                Add(element);
            }
        }
        /// 
        /// Requires: !IsReadOnly
        /// Removes given element from the set. If the set does not contain
        /// the element, does nothing.
        /// 
        internal void Remove(TElement element)
        {
            AssertReadWrite();
            _values.Remove(element);
        }
        /// 
        /// Requires: !IsReadOnly
        /// Removes all elements from the set.
        /// 
        internal void Clear()
        {
            AssertReadWrite();
            _values.Clear();
        }
        /// 
        /// Returns an array containing all elements of the set. Order is arbitrary.
        /// 
        internal TElement[] ToArray()
        {
            return _values.ToArray();
        }
        /// 
        /// Requires: other set must not be null and must have the same comparer.
        /// Returns true if this set contains the same elements as the other set.
        /// 
        internal bool SetEquals(Set other)
        {
            AssertSetCompatible(other);
            return _values.Count == other._values.Count
                && _values.IsSubsetOf(other._values);
        }
        /// 
        /// Requires: other set must not be null and must have the same comparer.
        /// Returns true if all elements in this set are contained in the other set.
        /// 
        internal bool IsSubsetOf(Set other)
        {
            AssertSetCompatible(other);
            return _values.IsSubsetOf(other._values);
        }
        /// 
        /// Requires: other set must not be null and must have the same comparer.
        /// Returns true if this set and other set have some elements in common.
        /// 
        internal bool Overlaps(Set other)
        {
            AssertSetCompatible(other);
            return _values.Overlaps(other._values);
        }
        /// 
        /// Requires: !IsReadOnly
        /// Requires: other collection must not be null.
        /// Subtracts other set from this set, leaving the result in this.
        /// 
        internal void Subtract(IEnumerable other)
        {
            AssertReadWrite();
            _values.ExceptWith(other);
        }
        /// 
        /// Requires: other collection must not be null.
        /// Subtracts other set from this set, returning result.
        /// 
        internal Set Difference(IEnumerable other)
        {
            Set copy = new Set(this);
            copy.Subtract(other);
            return copy;
        }
        /// 
        /// Requires: !IsReadOnly
        /// Requires: other collection must not be null.
        /// Unions other set with this set, leaving the result in this set.
        /// 
        internal void Unite(IEnumerable other)
        {
            AssertReadWrite();
            _values.UnionWith(other);
        }
        /// 
        /// Requires: other collection must not be null.
        /// Unions other set with this set, returning the result.
        /// 
        internal Set Union(IEnumerable other)
        {
            Set copy = new Set(this);
            copy.Unite(other);
            return copy;
        }
        /// 
        /// Requires: !IsReadOnly
        /// Requires: other set must not be null and must have the same comparer.
        /// Intersects this set and other set, leaving the result in this set.
        /// 
        internal void Intersect(Set other)
        {
            AssertReadWrite();
            AssertSetCompatible(other);
            _values.IntersectWith(other._values);
        }
        /// 
        /// Returns a readonly version of this set.
        /// 
        internal Set AsReadOnly()
        {
            if (_isReadOnly)
            {
                // once it's readonly, it's always readonly
                return this;
            }
            Set copy = new Set(this);
            copy._isReadOnly = true;
            return copy;
        }
        /// 
        /// Makes this set readonly and returns this set.
        /// 
        internal Set MakeReadOnly()
        {
            _isReadOnly = true;
            return this;
        }
        /// 
        /// Returns aggregate hash code of all elements in this set.
        /// 
        internal int GetElementsHashCode()
        {
            int hashCode = 0;
            foreach (TElement element in this)
            {
                hashCode ^= Comparer.GetHashCode(element);
            }
            return hashCode;
        }
        /// 
        /// Returns typed enumerator over elements of the set. 
        /// Uses HashSet<TElement>.Enumerator to avoid boxing struct.
        /// 
        public HashSet.Enumerator GetEnumerator() 
        { 
            return _values.GetEnumerator(); 
        }
        [Conditional("DEBUG")]
        private void AssertReadWrite()
        {
            Debug.Assert(!_isReadOnly, "attempting to modify readonly collection");
        }
        [Conditional("DEBUG")]
        private void AssertSetCompatible(Set other)
        {
            Debug.Assert(other != null, "other set null");
            Debug.Assert(other.Comparer.GetType().Equals(this.Comparer.GetType()));
        }
        #endregion
        #region IEnumerable Members
        public class Enumerator : IEnumerator
        {
            private Dictionary.KeyCollection.Enumerator keys;
            internal Enumerator(Dictionary.KeyCollection.Enumerator keys)
            {
                this.keys = keys;
            }
            public TElement Current { get { return keys.Current; } }
            public void Dispose() { keys.Dispose(); }
            object IEnumerator.Current { get { return ((IEnumerator)keys).Current; } }
            public bool MoveNext() { return keys.MoveNext(); }
            void System.Collections.IEnumerator.Reset() { ((System.Collections.IEnumerator)keys).Reset(); }
        }
        IEnumerator IEnumerable.GetEnumerator()
        {
            return this.GetEnumerator();
        }
        #endregion
        #region IEnumerable Members
        /// 
        /// Returns an untyped enumeration of elements in the set.
        /// 
        /// Enumeration of set members.
        IEnumerator IEnumerable.GetEnumerator()
        {
            return this.GetEnumerator();
        }
        #endregion
        #region InternalBase
        internal override void ToCompactString(StringBuilder builder)
        {
            StringUtil.ToCommaSeparatedStringSorted(builder, this);
        }
        #endregion
        #region Nested types
        private class SetValueComparer : IEqualityComparer>
        {
            bool IEqualityComparer>.Equals(Set x, Set y)
            {
                Debug.Assert(null != x && null != y, "comparer must be used only in context of Dictionary/HashSet");
                return x.SetEquals(y);
            }
            int IEqualityComparer>.GetHashCode(Set obj)
            {
                Debug.Assert(null != obj, "comparer must be used only in context of Dictionary/HashSet");
                return obj.GetElementsHashCode();
            }
        }
        #endregion
    }
}