/* ****************************************************************************
 *
 * Copyright (c) Microsoft Corporation. 
 *
 * This source code is subject to terms and conditions of the Microsoft Public License. A 
 * copy of the license can be found in the License.html file at the root of this distribution. If 
 * you cannot locate the  Microsoft Public License, please send an email to 
 * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound 
 * by the terms of the Microsoft Public License.
 *
 * You must not remove this notice, or any other, from this software.
 *
 *
 * ***************************************************************************/

using System.Collections;
using System.Collections.Generic;

namespace Microsoft.Scripting.Utils {

    /// <summary>
    /// Dictionary[TKey, TValue] is not thread-safe in the face of concurrent reads and writes. SynchronizedDictionary
    /// provides a thread-safe implementation. It holds onto a Dictionary[TKey, TValue] instead of inheriting from
    /// it so that users who need to do manual synchronization can access the underlying Dictionary[TKey, TValue].
    /// </summary>
    public class SynchronizedDictionary<TKey, TValue> :
        IDictionary<TKey, TValue>,
        ICollection<KeyValuePair<TKey, TValue>>,
        IEnumerable<KeyValuePair<TKey, TValue>> {

        Dictionary<TKey, TValue> _dictionary;

        /// <summary>
        /// This returns the raw unsynchronized Dictionary[TKey, TValue]. Users are responsible for locking
        /// on it before accessing it. Also, it should not be arbitrarily handed out to other code since deadlocks
        /// can be caused if other code incorrectly locks on it.
        /// </summary>
        public Dictionary<TKey, TValue> UnderlyingDictionary {
            get { return _dictionary; }
        }

        public SynchronizedDictionary() 
            : this(new Dictionary<TKey, TValue>()) {
        }

        public SynchronizedDictionary(Dictionary<TKey, TValue> dictionary) {
            _dictionary = dictionary;
        }

        #region IDictionary<TKey,TValue> Members

        public void Add(TKey key, TValue value) {
            lock (_dictionary) {
                _dictionary.Add(key, value);
            }
        }

        public bool ContainsKey(TKey key) {
            lock (_dictionary) {
                return _dictionary.ContainsKey(key);
            }
        }

        public ICollection<TKey> Keys {
            get {
                lock (_dictionary) {
                    return _dictionary.Keys;
                }
            }
        }

        public bool Remove(TKey key) {
            lock (_dictionary) {
                return _dictionary.Remove(key);
            }
        }

        public bool TryGetValue(TKey key, out TValue value) {
            lock (_dictionary) {
                return _dictionary.TryGetValue(key, out value);
            }
        }

        public ICollection<TValue> Values {
            get {
                lock (_dictionary) {
                    return _dictionary.Values;
                }
            }
        }

        public TValue this[TKey key] {
            get {
                lock (_dictionary) {
                    return _dictionary[key];
                }
            }
            set {
                lock (_dictionary) {
                    _dictionary[key] = value;
                }
            }
        }

        #endregion

        #region ICollection<KeyValuePair<TKey,TValue>> Members

        private ICollection<KeyValuePair<TKey, TValue>> AsICollection() {
            return ((ICollection<KeyValuePair<TKey, TValue>>)_dictionary);
        }

        public void Add(KeyValuePair<TKey, TValue> item) {
            lock (_dictionary) {
                AsICollection().Add(item);
            }
        }

        public void Clear() {
            lock (_dictionary) {
                AsICollection().Clear();
            }
        }

        public bool Contains(KeyValuePair<TKey, TValue> item) {
            lock (_dictionary) {
                return AsICollection().Contains(item);
            }
        }

        public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex) {
            lock (_dictionary) {
                AsICollection().CopyTo(array, arrayIndex);
            }
        }

        public int Count {
            get {
                lock (_dictionary) {
                    return AsICollection().Count;
                }
            }
        }

        public bool IsReadOnly {
            get {
                lock (_dictionary) {
                    return AsICollection().IsReadOnly;
                }
            }
        }

        public bool Remove(KeyValuePair<TKey, TValue> item) {
            lock (_dictionary) {
                return AsICollection().Remove(item);
            }
        }

        #endregion

        #region IEnumerable<KeyValuePair<TKey,TValue>> Members

        public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() {
            lock (_dictionary) {
                return _dictionary.GetEnumerator();
            }
        }

        #endregion

        #region IEnumerable Members

        IEnumerator IEnumerable.GetEnumerator() {
            lock (_dictionary) {
                return _dictionary.GetEnumerator();
            }
        }

        #endregion
    }
}
