using System;

namespace NadekoBot.Core.Common.Caching
{
    /// <summary>
    /// A caching object which loads its value with a factory method when it expires.
    /// </summary>
    /// <typeparam name="T">Type of the value which is cached.</typeparam>
    public class FactoryCache<T> : IFactoryCache
    {
        public DateTime LastUpdate { get; set; } = DateTime.MinValue;
        private readonly object _locker = new object();
        private TimeSpan _expireAfter;
        private readonly Func<T> _factory;
        private T Value;

        /// <summary>
        /// Creates a new factory cache object.
        /// </summary>
        /// <param name="factory">Method which loads the value when it expires or if it's not loaded the first time.</param>
        /// <param name="expireAfter">Time after which the value will be reloaded.</param>
        /// <param name="loadImmediately">Should the value be loaded right away. If set to false, value will load when it's first retrieved.</param>
        public FactoryCache(Func<T> factory, TimeSpan expireAfter, 
            bool loadImmediately = false)
        {
            _expireAfter = expireAfter;
            _factory = factory;
            if (loadImmediately)
            {
                Value = _factory();
                LastUpdate = DateTime.UtcNow;
            }
        }

        public T GetValue()
        {
            lock (_locker)
            {
                if (DateTime.UtcNow - LastUpdate > _expireAfter)
                {
                    LastUpdate = DateTime.UtcNow;
                    return Value = _factory();
                }

                return Value;
            }
        }
    }
}