using Quadarax.Foundation.Core.Business.Processor.Enums;
using Quadarax.Foundation.Core.Business.Processor.Task;
using Quadarax.Foundation.Core.Logging;

namespace Quadarax.Foundation.Core.Business.Processor.Queue
{
    internal class TaskSlots
    {
        #region *** Properties ***
        public int FreeSlotsCount => _taskSlots.Count(x => x == null);
        #endregion

        #region *** Fields ***
        private ITaskItem?[] _taskSlots;
        private readonly ILog _log;
        #endregion

        #region *** Indexer ***
        public ITaskItem? this[int ordinal] => GetSlot(ordinal);
        #endregion

        #region *** Constructor ***
        public TaskSlots(int initialMaxSlots)
        {
            _taskSlots = new ITaskItem?[initialMaxSlots];
            _log = new ConsoleLogger().GetLogger(nameof(TaskSlots));
        }
        #endregion

        #region *** Public Operations ***
        public int AssignSlot(ITaskItem task)
        {
            if(task == null) throw new ArgumentNullException(nameof(task));

            var ordinal = GetNextFreeSlotOrdinal();
            if (ordinal  == -1) return -1;
            _taskSlots[ordinal] = task;
        
            _log.Log(LogSeverityEnum.Debug, $"Task ({task}) assigned to slot # {ordinal}");

            return ordinal;
        }

        public ITaskItem? GetSlot(int ordinal)
        {
            if (ordinal < 0 || ordinal >= _taskSlots.Length) throw new ArgumentOutOfRangeException(nameof(ordinal));
            return _taskSlots[ordinal];
        }

        public void ReleaseSlot(int ordinal)
        {

            var task = GetSlot(ordinal);
            if (task == null) return;

            if (task.Status == ProcessStatusEnum.Started)
                task.Stop("(System) Paused due change configuration.", true, false);

            _taskSlots[ordinal] = null;
            _log.Log(LogSeverityEnum.Debug, $"Task ({task}) was released from slot # {ordinal}");
        }

        public void ReleaseAll()
        {
            _log.Log(LogSeverityEnum.Debug, "Releasing all slots ...");
            for (var a = 0; a < _taskSlots.Length; a++)
                ReleaseSlot(a);
        }

        public void SetMaxSlots(int maxSlots)
        {
            _log.Log(LogSeverityEnum.Debug, $"Set max slots from {_taskSlots.Length} to {maxSlots}");
            if (maxSlots < 1) throw new ArgumentOutOfRangeException(nameof(maxSlots));
            ReleaseAll();
            _taskSlots = new ITaskItem[maxSlots];
        }
        #endregion

        #region *** Private Operations ***
        private int GetNextFreeSlotOrdinal()
        {
            for (var i = 0; i < _taskSlots.Length; i++)
            {
                if (_taskSlots[i] == null) return i;
            }
            return -1;
        }
        #endregion
    }
}
