using InABox.Core; using System; using System.Collections.Generic; using System.Text; namespace Comal.Classes { public static class OvertimeUtils { public delegate void EvaluateOvertimeDelegate(TBlock block, OvertimeInterval? interval, TimeSpan duration); /// /// Evaluate the overtime intervals given, using as the list of blocks that have been worked for this day. /// is used to get the duration of each , and /// is called for every distinct block that needs evaluating. So if a given /// overlaps with multiple overtime intervals, will be called for each interval, with the length of time overlapped /// with that interval provided. /// /// /// The interval passed to should never be ; it is only if all the overtime /// is used up and there is no interval of type ; this is not allowed, so it shouldn't happen. Nevertheless, /// the case should be accounted for in case of bad data. /// public static void EvaluateOvertime( IEnumerable blocks, IList overtimeIntervals, Func durationSelector, EvaluateOvertimeDelegate evaluateBlock ) { var curOvertimeIdx = 0; OvertimeInterval? GetOvertimeInterval() => curOvertimeIdx < overtimeIntervals.Count ? overtimeIntervals[curOvertimeIdx] : null; var curInterval = GetOvertimeInterval()?.Interval ?? TimeSpan.Zero; foreach (var block in blocks) { var duration = durationSelector(block); while (duration > TimeSpan.Zero) { var interval = GetOvertimeInterval(); if (interval != null) { switch (interval.IntervalType) { case OvertimeIntervalType.Interval: if (duration >= curInterval) { // In this case, the block is more than the rest of // the current interval, so we use up all the remaining interval // time, and then move to the next interval. evaluateBlock(block, interval, curInterval); duration -= curInterval; ++curOvertimeIdx; curInterval = GetOvertimeInterval()?.Interval ?? TimeSpan.Zero; } else { evaluateBlock(block, interval, duration); // Otherwise, we use up the entire block, and decrease the interval by the duration remaining. curInterval -= duration; duration = TimeSpan.Zero; } break; case OvertimeIntervalType.RemainingTime: // In this case, the interval is unchanged. evaluateBlock(block, interval, duration); duration = TimeSpan.Zero; break; default: throw new NotImplementedException($"Not implemented Overtime interval type {interval.IntervalType}"); } } else { // If there is no overtime interval, then we use up the rest of the time on // the block with a blank PayrollID. Theoretically, this shouldn't happen, // since the "RemainingTime" interval is required. Logger.Send(LogType.Error, "", $"Error with overtime intervals; no interval of type RemainingTime"); evaluateBlock(block, null, duration); duration = TimeSpan.Zero; } } } } } }