#region COPYRIGHT
//
//     THIS IS GENERATED BY TEMPLATE
//     
//     AUTHOR  :     ROYE
//     DATE       :     2010
//
//     COPYRIGHT (C) 2010, TIANXIAHOTEL TECHNOLOGIES CO., LTD. ALL RIGHTS RESERVED.
//
#endregion

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

namespace System.Caching
{
    public sealed class Cache<TKey, TValue>
    {
        private readonly Dictionary<TKey, object> _Entries;
        private int _Owner;

        public Cache()
        {
            _Entries = new Dictionary<TKey, object>();
        }

        public Cache(IEqualityComparer<TKey> equalityComparer)
        {
            _Entries = new Dictionary<TKey, object>(equalityComparer);
        }

        /// <summary>
        /// Insert a collectible object into the cache.
        /// </summary>
        /// <param name="key">The cache key used to reference the item.</param>
        /// <param name="value">The object to be inserted into the cache.</param>
        public void Insert(TKey key, TValue value)
        {
            Insert(key, value, CacheStrategy.Temporary);
        }

        /// <summary>
        /// Insert an object into the cache using the specified cache strategy (lifetime management).
        /// </summary>
        /// <param name="key">The cache key used to reference the item.</param>
        /// <param name="value">The object to be inserted into the cache.</param>
        /// <param name="strategy">The strategy to apply for the inserted item (use Temporary for objects 
        /// that are collectible and Permanent for objects you wish to keep forever).</param>
        public void Insert(TKey key, TValue value, CacheStrategy strategy)
        {
            object entry = (strategy == CacheStrategy.Temporary) ? new WeakReference(value) : value as object;
            int current = Thread.CurrentThread.ManagedThreadId;
            while (Interlocked.CompareExchange(ref _Owner, current, 0) != current) { }
            _Entries[key] = entry;
            if (current != Interlocked.Exchange(ref _Owner, 0))
            {
                throw new UnauthorizedAccessException("Thread had access to cache even though it shouldn't have.");
            }
        }

        /// <summary>
        /// Retrieves an entry from the cache using the given key.
        /// </summary>
        /// <param name="key">The cache key of the item to retrieve.</param>
        /// <returns>The retrieved cache item or null if not found.</returns>
        public TValue Get(TKey key)
        {
            int current = Thread.CurrentThread.ManagedThreadId;
            while (Interlocked.CompareExchange(ref _Owner, current, 0) != current) { }
            object entry;
            _Entries.TryGetValue(key, out entry);
            if (current != Interlocked.Exchange(ref _Owner, 0))
            {
                throw new UnauthorizedAccessException("Thread had access to cache even though it shouldn't have.");
            }
            var wr = entry as WeakReference;
            return (TValue)(wr != null ? wr.Target : entry);
        }

        /// <summary>
        /// Removes the object associated with the given key from the cache.
        /// </summary>
        /// <param name="key">The cache key of the item to remove.</param>
        /// <returns>True if an item removed from the cache and false otherwise.</returns>
        public bool Remove(TKey key)
        {
            int current = Thread.CurrentThread.ManagedThreadId;
            while (Interlocked.CompareExchange(ref _Owner, current, 0) != current) { }
            bool found = _Entries.Remove(key);
            if (current != Interlocked.Exchange(ref _Owner, 0))
            {
                throw new UnauthorizedAccessException("Thread had access to cache even though it shouldn't have.");
            }
            return found;
        }

        /// <summary>
        /// Removes all entries from the cache.
        /// </summary>
        public void Clear()
        {
            int current = Thread.CurrentThread.ManagedThreadId;
            while (Interlocked.CompareExchange(ref _Owner, current, 0) != current) { }
            _Entries.Clear();
            if (current != Interlocked.Exchange(ref _Owner, 0))
            {
                throw new UnauthorizedAccessException("Thread had access to cache even though it shouldn't have.");
            }
        }

        /// <summary>
        /// Process all entries in the cache and remove entries that refer to collected entries.
        /// </summary>
        /// <returns>The number of live cache entries still in the cache.</returns>
        private int ClearCollected()
        {
            int current = Thread.CurrentThread.ManagedThreadId;
            while (Interlocked.CompareExchange(ref _Owner, current, 0) != current) { }
            IList<TKey> keys = _Entries.Where(kvp => kvp.Value is WeakReference && !(kvp.Value as WeakReference).IsAlive)
                .Select(kvp => kvp.Key)
                .ToList();
            keys.ForEach(k => _Entries.Remove(k));
            int count = _Entries.Count;
            if (current != Interlocked.Exchange(ref _Owner, 0))
            {
                throw new UnauthorizedAccessException("Thread had access to cache even though it shouldn't have.");
            }
            return count;
        }

        /// <summary>
        /// Returns the number of entries currently stored in the cache. Accessing this property
        /// causes a check of all entries in the cache to ensure collected entries are not counted.
        /// </summary>
        public int Count
        {
            get { return ClearCollected(); }
        }

        /// <summary>
        /// Indexer for accessing or adding cache entries.
        /// </summary>
        public TValue this[TKey key]
        {
            get { return Get(key); }
            set { Insert(key, value, CacheStrategy.Temporary); }
        }

        /// <summary>
        /// Indexer for adding a cache item using the specified strategy.
        /// </summary>
        public TValue this[TKey key, CacheStrategy strategy]
        {
            set { Insert(key, value, strategy); }
        }

        /// <summary>
        /// This method returns a string with information on the cache contents (number of contained objects).
        /// </summary>
        public override string ToString()
        {
            int count = ClearCollected();
            return count > 0 ? String.Format("Cache contains {0} live objects.", count) : "Cache is empty.";
        }
    }
}
