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

namespace Quadarax.Foundation.Common.Cache
{
    public class VolatileCache<TKey, TValue> : Cache<TKey, TValue>
    {
        private ConcurrentDictionary<TKey, TValue> cache = new ConcurrentDictionary<TKey, TValue>();

        #region *** Properties ***

        public int Count
        {
            get
            {
                return this.cache.Count;
            }
        }

        public TKey[] Keys
        {
            get
            {
                ICollection<TKey> keys = this.cache.Keys;
                TKey[] array = new TKey[keys.Count];
                keys.CopyTo(array, 0);
                return array;
            }
        }

        public TValue[] Values
        {
            get
            {
                ICollection<TValue> values = this.cache.Values;
                TValue[] array = new TValue[values.Count];
                values.CopyTo(array, 0);
                return array;
            }
        }

        #endregion

        #region *** Constructors ***

        public VolatileCache()
        {
            this.Enabled = true;
        }

        #endregion
        
        #region *** Overrides ***

        protected override TValue GetValue(TKey key)
        {
            TValue v;
            if (this.cache.TryGetValue(key, out v))
                return v;
            return default(TValue);
        }

        protected override void SetValue(TKey key, TValue value)
        {
            this.cache[key] = value;
        }

        protected override bool RemoveByKey(TKey key)
        {
            TValue v;
            return this.cache.TryRemove(key, out v);
        }

        public override bool Invalidate(TKey key)
        {
            return this.Remove(key);
        }

        #endregion

        public bool ContainsKey(TKey key)
        {
            return this.cache.ContainsKey(key);
        }

        public bool FindAndAddValue(TKey key, TValue value)
        {
            return this.cache.TryAdd(key, value);
        }

        public bool Remove(TKey key, out TValue value)
        {
            value = default(TValue);
            if (!this.Enabled)
                return false;
            return this.cache.TryRemove(key, out value);
        }

        public void Clear()
        {
            this.cache = new ConcurrentDictionary<TKey, TValue>();
        }

        public bool TryGetValue(TKey key, out TValue retval)
        {
            return this.cache.TryGetValue(key, out retval);
        }

        public bool TryGetValueOrFetchDummyValue(TKey key, Func<TValue> fetchDummyValueDelegate, out TValue retval)
        {
            bool flag = this.TryGetValue(key, out retval);
            if (!flag && fetchDummyValueDelegate != null)
            {
                flag = this.cache.TryGetValue(key, out retval);
                if (!flag)
                {
                    retval = fetchDummyValueDelegate();
                    this.cache[key] = retval;
                }
            }
            return flag;
        }

        public TValue[] GetAllValuesAndClear()
        {
            ICollection<TValue> values = this.cache.Values;
            TValue[] array = new TValue[values.Count];
            values.CopyTo(array, 0);
            this.Clear();
            return array;
        }
    }
}