/*
 * Copyright (c) Contributors, http://opensimulator.org/
 * See CONTRIBUTORS.TXT for a full list of copyright holders.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the OpenSimulator Project nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

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

namespace OpenSim.Framework
{
    /// <summary>
    /// Synchronized Cenome cache wrapper.
    /// </summary>
    /// <typeparam name="TKey">
    /// The type of keys in the cache. 
    /// </typeparam>
    /// <typeparam name="TValue">
    /// The type of values in the cache. 
    /// </typeparam>
    /// <remarks>
    /// <para>
    /// Enumerator will block other threads, until enumerator's <see cref="IDisposable.Dispose"/> method is called.
    /// "foreach" statement is automatically calling it.
    /// </para>
    /// </remarks>
    public class CnmSynchronizedCache<TKey, TValue> : ICnmCache<TKey, TValue>
    {
        /// <summary>
        /// The cache object.
        /// </summary>
        private readonly ICnmCache<TKey, TValue> m_cache;

        /// <summary>
        /// Synchronization root.
        /// </summary>
        private readonly object m_syncRoot;

        /// <summary>
        /// Initializes a new instance of the <see cref="CnmSynchronizedCache{TKey,TValue}"/> class. 
        /// Initializes a new instance of the <see cref="CnmSynchronizedCache{TKey,TValue}"/> class.
        /// </summary>
        /// <param name="cache">
        /// The cache.
        /// </param>
        private CnmSynchronizedCache(ICnmCache<TKey, TValue> cache)
        {
            m_cache = cache;
            m_syncRoot = m_cache.SyncRoot;
        }

        /// <summary>
        /// Returns a <see cref="ICnmCache{TKey,TValue}"/> wrapper that is synchronized (thread safe). 
        /// </summary>
        /// <param name="cache">
        /// The <see cref="ICnmCache{TKey,TValue}"/> to synchronize.
        /// </param>
        /// <returns>
        /// A <see cref="ICnmCache{TKey,TValue}"/> wrapper that is synchronized (thread safe). 
        /// </returns>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="cache"/>is null.
        /// </exception>
        public static ICnmCache<TKey, TValue> Synchronized(ICnmCache<TKey, TValue> cache)
        {
            if (cache == null)
                throw new ArgumentNullException("cache");
            return cache.IsSynchronized ? cache : new CnmSynchronizedCache<TKey, TValue>(cache);
        }

        #region Nested type: SynchronizedEnumerator

        /// <summary>
        /// Synchronized enumerator.
        /// </summary>
        private class SynchronizedEnumerator : IEnumerator<KeyValuePair<TKey, TValue>>
        {
            /// <summary>
            /// Enumerator that is being synchronized.
            /// </summary>
            private readonly IEnumerator<KeyValuePair<TKey, TValue>> m_enumerator;

            /// <summary>
            /// Synchronization root.
            /// </summary>
            private object m_syncRoot;

            /// <summary>
            /// Initializes a new instance of the <see cref="SynchronizedEnumerator"/> class.
            /// </summary>
            /// <param name="enumerator">
            /// The enumerator that is being synchronized.
            /// </param>
            /// <param name="syncRoot">
            /// The sync root.
            /// </param>
            public SynchronizedEnumerator(IEnumerator<KeyValuePair<TKey, TValue>> enumerator, object syncRoot)
            {
                m_syncRoot = syncRoot;
                m_enumerator = enumerator;
                Monitor.Enter(m_syncRoot);
            }

            /// <summary>
            /// Finalizes an instance of the <see cref="SynchronizedEnumerator"/> class. 
            /// </summary>
            ~SynchronizedEnumerator()
            {
                Dispose();
            }

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

            /// <summary>
            /// Gets the element in the collection at the current position of the enumerator.
            /// </summary>
            /// <returns>
            /// The element in the collection at the current position of the enumerator.
            /// </returns>
            /// <exception cref="InvalidOperationException">
            /// The enumerator has reach end of collection or <see cref="MoveNext"/> is not called.
            /// </exception>
            public KeyValuePair<TKey, TValue> Current
            {
                get { return m_enumerator.Current; }
            }

            /// <summary>
            /// Gets the current element in the collection.
            /// </summary>
            /// <returns>
            /// The current element in the collection.
            /// </returns>
            /// <exception cref="InvalidOperationException">
            /// The enumerator is positioned before the first element of the collection or after the last element.
            /// </exception><filterpriority>2</filterpriority>
            object IEnumerator.Current
            {
                get { return Current; }
            }

            /// <summary>
            /// Releases synchronization lock.
            /// </summary>
            public void Dispose()
            {
                if (m_syncRoot != null)
                {
                    Monitor.Exit(m_syncRoot);
                    m_syncRoot = null;
                }

                m_enumerator.Dispose();
                GC.SuppressFinalize(this);
            }

            /// <summary>
            /// Advances the enumerator to the next element of the collection.
            /// </summary>
            /// <returns>
            /// true if the enumerator was successfully advanced to the next element; false if the enumerator has passed the end of the collection.
            /// </returns>
            /// <exception cref="InvalidOperationException">
            /// The collection was modified after the enumerator was created. 
            /// </exception>
            public bool MoveNext()
            {
                return m_enumerator.MoveNext();
            }

            /// <summary>
            /// Sets the enumerator to its initial position, which is before the first element in the collection.
            /// </summary>
            /// <exception cref="InvalidOperationException">
            /// The collection was modified after the enumerator was created. 
            /// </exception>
            public void Reset()
            {
                m_enumerator.Reset();
            }

            #endregion
        }

        #endregion

        #region ICnmCache<TKey,TValue> Members

        /// <summary>
        /// Gets current count of elements stored to <see cref="ICnmCache{TKey,TValue}"/>.
        /// </summary>
        /// <remarks>
        /// <para>
        /// When adding an new element to <see cref="ICnmCache{TKey,TValue}"/> that is limiting element count, 
        /// <see cref="ICnmCache{TKey,TValue}"/> will remove less recently used elements until it can fit an new element. 
        /// </para>
        /// </remarks>
        /// <seealso cref="ICnmCache{TKey,TValue}.MaxCount"/>
        /// <seealso cref="ICnmCache{TKey,TValue}.IsCountLimited"/>
        /// <seealso cref="ICnmCache{TKey,TValue}.IsSizeLimited"/>
        /// <seealso cref="ICnmCache{TKey,TValue}.IsTimeLimited"/>
        public int Count
        {
            get
            {
                lock (m_syncRoot)
                {
                    return m_cache.Count;
                }
            }
        }

        /// <summary>
        /// Gets or sets elements expiration time.
        /// </summary>
        /// <value>
        /// Elements expiration time.
        /// </value>
        /// <remarks>
        /// <para>
        /// When element has been stored in <see cref="ICnmCache{TKey,TValue}"/> longer than <see cref="ICnmCache{TKey,TValue}.ExpirationTime"/> 
        /// and it is not accessed through <see cref="ICnmCache{TKey,TValue}.TryGetValue"/> method or element's value is 
        /// not replaced by <see cref="ICnmCache{TKey,TValue}.Set"/> method, then it is automatically removed from the 
        /// <see cref="ICnmCache{TKey,TValue}"/>.
        /// </para>
        /// <para>
        /// It is possible that <see cref="ICnmCache{TKey,TValue}"/> implementation removes element before it's expiration time, 
        /// because total size or count of elements stored to cache is larger than <see cref="ICnmCache{TKey,TValue}.MaxSize"/> or <see cref="ICnmCache{TKey,TValue}.MaxCount"/>.
        /// </para>
        /// <para>
        /// It is also possible that element stays in cache longer than <see cref="ICnmCache{TKey,TValue}.ExpirationTime"/>.
        /// </para>
        /// <para>
        /// Calling <see cref="ICnmCache{TKey,TValue}.PurgeExpired"/> try to remove all elements that are expired.
        /// </para>
        /// <para>
        /// To disable time limit in cache, set <see cref="ICnmCache{TKey,TValue}.ExpirationTime"/> to <see cref="DateTime.MaxValue"/>.
        /// </para>
        /// </remarks>
        /// <seealso cref="ICnmCache{TKey,TValue}.IsTimeLimited"/>
        /// <seealso cref="ICnmCache{TKey,TValue}.IsCountLimited"/>
        /// <seealso cref="ICnmCache{TKey,TValue}.IsSizeLimited"/>
        /// <seealso cref="ICnmCache{TKey,TValue}.PurgeExpired"/>
        /// <seealso cref="ICnmCache{TKey,TValue}.Count"/>
        /// <seealso cref="ICnmCache{TKey,TValue}.MaxCount"/>
        /// <seealso cref="ICnmCache{TKey,TValue}.MaxSize"/>
        /// <seealso cref="ICnmCache{TKey,TValue}.Size"/>
        public TimeSpan ExpirationTime
        {
            get
            {
                lock (m_syncRoot)
                {
                    return m_cache.ExpirationTime;
                }
            }

            set
            {
                lock (m_syncRoot)
                {
                    m_cache.ExpirationTime = value;
                }
            }
        }

        /// <summary>
        /// Gets a value indicating whether <see cref="ICnmCache{TKey,TValue}"/> is limiting count of elements.
        /// </summary>
        /// <value>
        /// <see langword="true"/> if the <see cref="ICnmCache{TKey,TValue}"/> count of elements is limited; 
        /// otherwise, <see langword="false"/>. 
        /// </value>
        /// <remarks>
        /// <para>
        /// When adding an new element to <see cref="ICnmCache{TKey,TValue}"/> that is limiting element count, 
        /// <see cref="ICnmCache{TKey,TValue}"/> will remove less recently used elements until it can fit an new element. 
        /// </para>
        /// </remarks>
        /// <seealso cref="ICnmCache{TKey,TValue}.Count"/>
        /// <seealso cref="ICnmCache{TKey,TValue}.MaxCount"/> 
        /// <seealso cref="ICnmCache{TKey,TValue}.IsSizeLimited"/>
        /// <seealso cref="ICnmCache{TKey,TValue}.IsTimeLimited"/>
        public bool IsCountLimited
        {
            get
            {
                lock (m_syncRoot)
                {
                    return m_cache.IsCountLimited;
                }
            }
        }

        /// <summary>
        /// Gets a value indicating whether <see cref="ICnmCache{TKey,TValue}"/> is limiting size of elements.
        /// </summary>
        /// <value>
        /// <see langword="true"/> if the <see cref="ICnmCache{TKey,TValue}"/> total size of elements is limited; 
        /// otherwise, <see langword="false"/>. 
        /// </value>
        /// <remarks>
        /// <para>
        /// When adding an new element to <see cref="ICnmCache{TKey,TValue}"/> that is limiting total size of elements, 
        /// <see cref="ICnmCache{TKey,TValue}"/> will remove less recently used elements until it can fit an new element. 
        /// </para>
        /// </remarks>
        /// <seealso cref="ICnmCache{TKey,TValue}.MaxElementSize"/>
        /// <seealso cref="ICnmCache{TKey,TValue}.Size"/>
        /// <seealso cref="ICnmCache{TKey,TValue}.MaxSize"/>
        /// <seealso cref="ICnmCache{TKey,TValue}.IsCountLimited"/>
        /// <seealso cref="ICnmCache{TKey,TValue}.IsTimeLimited"/>
        public bool IsSizeLimited
        {
            get
            {
                lock (m_syncRoot)
                {
                    return m_cache.IsSizeLimited;
                }
            }
        }

        /// <summary>
        /// Gets a value indicating whether or not access to the <see cref="ICnmCache{TKey,TValue}"/> is synchronized (thread safe).
        /// </summary>
        /// <value>
        /// <see langword="true"/> if access to the <see cref="ICnmCache{TKey,TValue}"/> is synchronized (thread safe); 
        /// otherwise, <see langword="false"/>. 
        /// </value>
        /// <remarks>
        /// <para>
        /// To get synchronized (thread safe) access to <see cref="ICnmCache{TKey,TValue}"/> object, use 
        /// <see cref="CnmSynchronizedCache{TKey,TValue}.Synchronized"/> in <see cref="CnmSynchronizedCache{TKey,TValue}"/> class
        /// to retrieve synchronized wrapper for <see cref="ICnmCache{TKey,TValue}"/> object.
        /// </para>
        /// </remarks>
        /// <seealso cref="ICnmCache{TKey,TValue}.SyncRoot"/>
        /// <seealso cref="CnmSynchronizedCache{TKey,TValue}"/>
        public bool IsSynchronized
        {
            get { return true; }
        }

        /// <summary>
        /// Gets a value indicating whether elements stored to <see cref="ICnmCache{TKey,TValue}"/> have limited inactivity time.
        /// </summary>
        /// <value>
        /// <see langword="true"/> if the <see cref="ICnmCache{TKey,TValue}"/> has a fixed total size of elements; 
        /// otherwise, <see langword="false"/>. 
        /// </value>
        /// <remarks>
        /// If <see cref="ICnmCache{TKey,TValue}"/> have limited inactivity time and element is not accessed through <see cref="ICnmCache{TKey,TValue}.Set"/>
        /// or <see cref="ICnmCache{TKey,TValue}.TryGetValue"/> methods in <see cref="ICnmCache{TKey,TValue}.ExpirationTime"/> , then element is automatically removed from 
        /// the cache. Depending on implementation of the <see cref="ICnmCache{TKey,TValue}"/>, some of the elements may 
        /// stay longer in cache.
        /// </remarks>
        /// <seealso cref="ICnmCache{TKey,TValue}.ExpirationTime"/>
        /// <seealso cref="ICnmCache{TKey,TValue}.PurgeExpired"/>
        /// <seealso cref="ICnmCache{TKey,TValue}.IsCountLimited"/>
        /// <seealso cref="ICnmCache{TKey,TValue}.IsSizeLimited"/>
        public bool IsTimeLimited
        {
            get
            {
                lock (m_syncRoot)
                {
                    return m_cache.IsTimeLimited;
                }
            }
        }

        /// <summary>
        /// Gets or sets maximal allowed count of elements that can be stored to <see cref="ICnmCache{TKey,TValue}"/>.
        /// </summary>
        /// <value>
        /// <see cref="int.MaxValue"/>, if <see cref="ICnmCache{TKey,TValue}"/> is not limited by count of elements; 
        /// otherwise maximal allowed count of elements.
        /// </value>
        /// <remarks>
        /// <para>
        /// When adding an new element to <see cref="ICnmCache{TKey,TValue}"/> that is limiting element count, 
        /// <see cref="ICnmCache{TKey,TValue}"/> will remove less recently used elements until it can fit an new element. 
        /// </para>
        /// </remarks>
        public int MaxCount
        {
            get
            {
                lock (m_syncRoot)
                {
                    return m_cache.MaxCount;
                }
            }

            set
            {
                lock (m_syncRoot)
                {
                    m_cache.MaxCount = value;
                }
            }
        }

        /// <summary>
        /// <para>Gets maximal allowed element size.</para>
        /// </summary>
        /// <value>
        /// Maximal allowed element size.
        /// </value>
        /// <remarks>
        /// <para>
        /// If element's size is larger than <see cref="ICnmCache{TKey,TValue}.MaxElementSize"/>, then element is 
        /// not added to the <see cref="ICnmCache{TKey,TValue}"/>.
        /// </para>
        /// </remarks>
        /// <seealso cref="ICnmCache{TKey,TValue}.Set"/>
        /// <seealso cref="ICnmCache{TKey,TValue}.IsSizeLimited"/>
        /// <seealso cref="ICnmCache{TKey,TValue}.Size"/>
        /// <seealso cref="ICnmCache{TKey,TValue}.MaxSize"/>
        public long MaxElementSize
        {
            get
            {
                lock (m_syncRoot)
                {
                    return m_cache.MaxElementSize;
                }
            }
        }

        /// <summary>
        /// Gets or sets maximal allowed total size for elements stored to <see cref="ICnmCache{TKey,TValue}"/>.
        /// </summary>
        /// <value>
        /// Maximal allowed total size for elements stored to <see cref="ICnmCache{TKey,TValue}"/>.
        /// </value>
        /// <remarks>
        /// <para>
        /// Normally size is total bytes used by elements in the cache. But it can be any other suitable unit of measure.
        /// </para>
        /// <para>
        /// When adding an new element to <see cref="ICnmCache{TKey,TValue}"/> that is limiting total size of elements, 
        /// <see cref="ICnmCache{TKey,TValue}"/> will remove less recently used elements until it can fit an new element. 
        /// </para>
        /// </remarks>
        /// <exception cref="ArgumentOutOfRangeException">value is less than 0.</exception>
        /// <seealso cref="ICnmCache{TKey,TValue}.MaxElementSize"/>
        /// <seealso cref="ICnmCache{TKey,TValue}.IsSizeLimited"/>
        /// <seealso cref="ICnmCache{TKey,TValue}.Size"/>
        public long MaxSize
        {
            get
            {
                lock (m_syncRoot)
                {
                    return m_cache.MaxSize;
                }
            }

            set
            {
                lock (m_syncRoot)
                {
                    m_cache.MaxSize = value;
                }
            }
        }

        /// <summary>
        /// Gets total size of elements stored to <see cref="ICnmCache{TKey,TValue}"/>.
        /// </summary>
        /// <value>
        /// Total size of elements stored to <see cref="ICnmCache{TKey,TValue}"/>.
        /// </value>
        /// <remarks>
        /// <para>
        /// Normally bytes, but can be any suitable unit of measure.
        /// </para>
        /// <para>
        /// Element's size is given when element is added or replaced by <see cref="ICnmCache{TKey,TValue}.Set"/> method. 
        /// </para>
        /// <para>
        /// When adding an new element to <see cref="ICnmCache{TKey,TValue}"/> that is limiting total size of elements, 
        /// <see cref="ICnmCache{TKey,TValue}"/> will remove less recently used elements until it can fit an new element. 
        /// </para>
        /// </remarks>
        /// <seealso cref="ICnmCache{TKey,TValue}.MaxElementSize"/>
        /// <seealso cref="ICnmCache{TKey,TValue}.IsSizeLimited"/>
        /// <seealso cref="ICnmCache{TKey,TValue}.MaxSize"/>
        /// <seealso cref="ICnmCache{TKey,TValue}.IsCountLimited"/>
        /// <seealso cref="ICnmCache{TKey,TValue}.ExpirationTime"/>
        public long Size
        {
            get
            {
                lock (m_syncRoot)
                {
                    return m_cache.Size;
                }
            }
        }

        /// <summary>
        /// Gets an object that can be used to synchronize access to the <see cref="ICnmCache{TKey,TValue}"/>.
        /// </summary>
        /// <value>
        /// An object that can be used to synchronize access to the <see cref="ICnmCache{TKey,TValue}"/>.
        /// </value>
        /// <remarks>
        /// <para>
        /// To get synchronized (thread safe) access to <see cref="ICnmCache{TKey,TValue}"/>, use <see cref="CnmSynchronizedCache{TKey,TValue}"/> 
        /// method <see cref="CnmSynchronizedCache{TKey,TValue}.Synchronized"/> to retrieve synchronized wrapper interface to 
        /// <see cref="ICnmCache{TKey,TValue}"/>.
        /// </para>
        /// </remarks>
        /// <seealso cref="ICnmCache{TKey,TValue}.IsSynchronized"/>
        /// <seealso cref="CnmSynchronizedCache{TKey,TValue}"/>
        public object SyncRoot
        {
            get { return m_syncRoot; }
        }

        /// <summary>
        /// Removes all elements from the <see cref="ICnmCache{TKey,TValue}"/>.
        /// </summary>
        /// <seealso cref="ICnmCache{TKey,TValue}.Set"/>
        /// <seealso cref="ICnmCache{TKey,TValue}.Remove"/>
        /// <seealso cref="ICnmCache{TKey,TValue}.RemoveRange"/>
        /// <seealso cref="ICnmCache{TKey,TValue}.TryGetValue"/>
        /// <seealso cref="ICnmCache{TKey,TValue}.PurgeExpired"/>
        public void Clear()
        {
            lock (m_syncRoot)
            {
                m_cache.Clear();
            }
        }

        /// <summary>
        /// Returns an enumerator that iterates through the elements stored to <see cref="ICnmCache{TKey,TValue}"/>.
        /// </summary>
        /// <returns>
        /// A <see cref="IEnumerator{T}"/> that can be used to iterate through the collection.
        /// </returns>
        /// <filterpriority>1</filterpriority>
        public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
        {
            lock (m_syncRoot)
            {
                return new SynchronizedEnumerator(m_cache.GetEnumerator(), m_syncRoot);
            }
        }

        /// <summary>
        /// Purge expired elements from the <see cref="ICnmCache{TKey,TValue}"/>.
        /// </summary>
        /// <remarks>
        /// <para>
        /// Element becomes expired when last access time to it has been longer time than <see cref="ICnmCache{TKey,TValue}.ExpirationTime"/>.
        /// </para>
        /// <para>
        /// Depending on <see cref="ICnmCache{TKey,TValue}"/> implementation, some of expired elements
        /// may stay longer than <see cref="ICnmCache{TKey,TValue}.ExpirationTime"/> in the cache.
        /// </para>
        /// </remarks>
        /// <seealso cref="ICnmCache{TKey,TValue}.IsTimeLimited"/>
        /// <seealso cref="ICnmCache{TKey,TValue}.ExpirationTime"/>
        /// <seealso cref="ICnmCache{TKey,TValue}.Set"/>
        /// <seealso cref="ICnmCache{TKey,TValue}.Remove"/>
        /// <seealso cref="ICnmCache{TKey,TValue}.RemoveRange"/>
        /// <seealso cref="ICnmCache{TKey,TValue}.TryGetValue"/>
        /// <seealso cref="ICnmCache{TKey,TValue}.Clear"/>
        public void PurgeExpired()
        {
            lock (m_syncRoot)
            {
                m_cache.PurgeExpired();
            }
        }

        /// <summary>
        /// Removes element associated with <paramref name="key"/> from the <see cref="ICnmCache{TKey,TValue}"/>.
        /// </summary>
        /// <param name="key">
        /// The key that is associated with element to remove from the <see cref="ICnmCache{TKey,TValue}"/>.
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="key"/>is <see langword="null"/>.
        /// </exception>
        /// <seealso cref="ICnmCache{TKey,TValue}.Set"/>
        /// <seealso cref="ICnmCache{TKey,TValue}.RemoveRange"/>
        /// <seealso cref="ICnmCache{TKey,TValue}.TryGetValue"/>
        /// <seealso cref="ICnmCache{TKey,TValue}.Clear"/>
        /// <seealso cref="ICnmCache{TKey,TValue}.PurgeExpired"/>
        public void Remove(TKey key)
        {
            lock (m_syncRoot)
            {
                m_cache.Remove(key);
            }
        }

        /// <summary>
        /// Removes elements that are associated with one of <paramref name="keys"/> from the <see cref="ICnmCache{TKey,TValue}"/>.
        /// </summary>
        /// <param name="keys">
        /// The keys that are associated with elements to remove from the <see cref="ICnmCache{TKey,TValue}"/>.
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="keys"/>is <see langword="null"/>.
        /// </exception>
        /// <seealso cref="ICnmCache{TKey,TValue}.Set"/>
        /// <seealso cref="ICnmCache{TKey,TValue}.Remove"/>
        /// <seealso cref="ICnmCache{TKey,TValue}.TryGetValue"/>
        /// <seealso cref="ICnmCache{TKey,TValue}.Clear"/>
        /// <seealso cref="ICnmCache{TKey,TValue}.PurgeExpired"/>
        public void RemoveRange(IEnumerable<TKey> keys)
        {
            lock (m_syncRoot)
            {
                m_cache.RemoveRange(keys);
            }
        }

        /// <summary>
        /// Add or replace an element with the provided <paramref name="key"/>, <paramref name="value"/> and <paramref name="size"/> to 
        /// <see cref="ICnmCache{TKey,TValue}"/>.
        /// </summary>
        /// <param name="key">
        /// The object used as the key of the element. Can't be <see langword="null"/> reference.
        /// </param>
        /// <param name="value">
        /// The object used as the value of the element to add or replace. <see langword="null"/> is allowed.
        /// </param>
        /// <param name="size">
        /// The element's size. Normally bytes, but can be any suitable unit of measure.
        /// </param>
        /// <returns>
        /// <see langword="true"/>if element has been added successfully to the <see cref="ICnmCache{TKey,TValue}"/>; 
        /// otherwise <see langword="false"/>.
        /// </returns>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="key"/>is <see langword="null"/>.
        /// </exception>
        /// <exception cref="ArgumentOutOfRangeException">
        /// The element's <paramref name="size"/> is less than 0.
        /// </exception>
        /// <remarks>
        /// <para>
        /// If element's <paramref name="size"/> is larger than <see cref="ICnmCache{TKey,TValue}.MaxElementSize"/>, then element is 
        /// not added to the <see cref="ICnmCache{TKey,TValue}"/>, however - possible older element is 
        /// removed from the <see cref="ICnmCache{TKey,TValue}"/>. 
        /// </para>
        /// <para>
        /// When adding an new element to <see cref="ICnmCache{TKey,TValue}"/> that is limiting total size of elements, 
        /// <see cref="ICnmCache{TKey,TValue}"/>will remove less recently used elements until it can fit an new element. 
        /// </para>
        /// <para>
        /// When adding an new element to <see cref="ICnmCache{TKey,TValue}"/> that is limiting element count, 
        /// <see cref="ICnmCache{TKey,TValue}"/>will remove less recently used elements until it can fit an new element. 
        /// </para>
        /// </remarks>
        /// <seealso cref="ICnmCache{TKey,TValue}.IsSizeLimited"/>
        /// <seealso cref="ICnmCache{TKey,TValue}.IsCountLimited"/>
        /// <seealso cref="ICnmCache{TKey,TValue}.Remove"/>
        /// <seealso cref="ICnmCache{TKey,TValue}.RemoveRange"/>
        /// <seealso cref="ICnmCache{TKey,TValue}.TryGetValue"/>
        /// <seealso cref="ICnmCache{TKey,TValue}.Clear"/>
        /// <seealso cref="ICnmCache{TKey,TValue}.PurgeExpired"/>
        public bool Set(TKey key, TValue value, long size)
        {
            lock (m_syncRoot)
            {
                return m_cache.Set(key, value, size);
            }
        }

        /// <summary>
        /// Gets the <paramref name="value"/> associated with the specified <paramref name="key"/>.
        /// </summary>
        /// <returns>
        /// <see langword="true"/>if the <see cref="ICnmCache{TKey,TValue}"/> contains an element with 
        /// the specified key; otherwise, <see langword="false"/>.
        /// </returns>
        /// <param name="key">
        /// The key whose <paramref name="value"/> to get.
        /// </param>
        /// <param name="value">
        /// When this method returns, the value associated with the specified <paramref name="key"/>, 
        /// if the <paramref name="key"/> is found; otherwise, the 
        /// default value for the type of the <paramref name="value"/> parameter. This parameter is passed uninitialized.
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="key"/>is <see langword="null"/>.
        /// </exception>
        /// <seealso cref="ICnmCache{TKey,TValue}.Set"/>
        /// <seealso cref="ICnmCache{TKey,TValue}.Remove"/>
        /// <seealso cref="ICnmCache{TKey,TValue}.RemoveRange"/>
        /// <seealso cref="ICnmCache{TKey,TValue}.Clear"/>
        /// <seealso cref="ICnmCache{TKey,TValue}.PurgeExpired"/>
        public bool TryGetValue(TKey key, out TValue value)
        {
            lock (m_syncRoot)
            {
                return m_cache.TryGetValue(key, out value);
            }
        }

        /// <summary>
        /// Returns an enumerator that iterates through the elements stored to <see cref="ICnmCache{TKey,TValue}"/>.
        /// </summary>
        /// <returns>
        /// A <see cref="IEnumerator"/> that can be used to iterate through the collection.
        /// </returns>
        /// <filterpriority>1</filterpriority>
        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        #endregion
    }
}
