using System;
namespace Wayne.Lib.AsyncManager
{
    /// <summary>
    /// Async manager using Int as operation id.
    /// </summary>
    public class IntAsyncManager : AsyncManager<int>
    {
        private int currentValue;
        private readonly int maxValue;
        private readonly int wrapTo;

        /// <summary>
        /// Constructor.
        /// Uses a default timeout of 1 hour.
        /// </summary>
        /// <param name="id">The Id.</param>
        /// <param name="parentEntity">The parent entity.</param>
        public IntAsyncManager(int id, IIdentifiableEntity parentEntity)
            : this(id, parentEntity, ServiceContainerFactory.Create(), 0, int.MaxValue, 0)
        {
        }

        /// <summary>
        /// Constructor.
        /// Uses a default timeout of 1 hour.
        /// </summary>
        /// <param name="id">The Id.</param>
        /// <param name="parentEntity">The parent entity.</param>
        /// <param name="serviceLocator"></param>
        /// <param name="initialValue">The initial value.</param>
        /// <param name="maxValue">The maximum (included) value.</param>
        public IntAsyncManager(int id, IIdentifiableEntity parentEntity, IServiceLocator serviceLocator, int initialValue, int maxValue)
            : this(id, parentEntity, serviceLocator, initialValue, maxValue, initialValue)
        {
        }

        /// <summary>
        /// Constructor. 
        /// Uses a default timeout of 1 hour.
        /// </summary>
        /// <param name="id">The Id.</param>
        /// <param name="parentEntity">The parent entity.</param>
        /// <param name="serviceLocator"></param>
        /// <param name="initialValue">The initial value.</param>
        /// <param name="maxValue">The maximum (included) value.</param>
        /// <param name="wrapTo">This is the next value after reaching the maximum value.</param>
        public IntAsyncManager(int id, IIdentifiableEntity parentEntity, IServiceLocator serviceLocator, int initialValue, int maxValue, int wrapTo)
            : this(id, parentEntity, serviceLocator, initialValue, maxValue, wrapTo, TimeSpan.FromHours(1))
        {
        }

        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="id">The Id.</param>
        /// <param name="parentEntity">The parent entity.</param>
        /// <param name="serviceLocator"></param>
        /// <param name="initialValue">The initial value.</param>
        /// <param name="maxValue">The maximum (included) value.</param>
        /// <param name="wrapTo">This is the next value after reaching the maximum value.</param>
        /// <param name="cleanOutstandingOperationsOlderThan">Sets the maximum age an operation can achieve.
        /// Minimum value is 1 minute. If null no timer is created.
        /// </param>
        public IntAsyncManager(int id, IIdentifiableEntity parentEntity, IServiceLocator serviceLocator, int initialValue, int maxValue, int wrapTo, TimeSpan? cleanOutstandingOperationsOlderThan)
            : base(id, parentEntity, serviceLocator, cleanOutstandingOperationsOlderThan)
        {
            if (initialValue > maxValue)
                throw new ArgumentException("The initialValue may not be greater than the maxValue!");
            if (wrapTo > maxValue)
                throw new ArgumentException("The wrapTo may not be greater than the maxValue!");
            currentValue = initialValue;
            this.maxValue = maxValue;
            this.wrapTo = wrapTo;
        }

        /// <summary>
        /// Generates the next id.
        /// </summary>
        /// <returns></returns>
        protected override int CreateNextOperationId()
        {
            int value = currentValue;
            long nextLong = (long)currentValue + 1;
            if (nextLong > maxValue)
                nextLong = wrapTo;
            currentValue = (int)nextLong;
            return value;
        }

        /// <summary>
        /// Entity subtype
        /// </summary>
        public override string EntitySubType
        {
            get { return "int"; }
        }
    }
}