namespace Mbproxy.Proxy.Cache;

/// <summary>
/// Pure address-range-overlap matcher for FC06 / FC16 write invalidation.
///
/// <para>Half-open interval math: a write covering <c>[w, w + writeQty)</c> overlaps an
/// entry covering <c>[s, s + qty)</c> iff <c>w &lt; s + qty AND s &lt; w + writeQty</c>.
/// Adjacent-but-not-overlapping (write to <c>[10..15)</c> vs cached <c>[15..20)</c>) does
/// NOT match — register 15 is not in the cached range.</para>
///
/// <para>Scope is restricted to FC03 / FC04 keys; we never cache writes so invalidation
/// only applies to read entries. Different <c>unitId</c> bytes never invalidate each other
/// (multi-drop / gateway personalities behind a shared socket).</para>
/// </summary>
internal static class CacheInvalidator
{
    /// <summary>
    /// Returns every <see cref="CacheKey"/> in <paramref name="haystack"/> that satisfies:
    /// <list type="bullet">
    ///   <item><description><see cref="CacheKey.UnitId"/> equals <paramref name="unitId"/>.</description></item>
    ///   <item><description><see cref="CacheKey.Fc"/> is 0x03 or 0x04 (only read entries are evicted).</description></item>
    ///   <item><description>The key's range <c>[StartAddress, StartAddress + Qty)</c> overlaps
    ///         the write range <c>[writeStart, writeStart + writeQty)</c>.</description></item>
    /// </list>
    ///
    /// <para>Pure function; the returned enumeration is materialised so callers can mutate
    /// the haystack while iterating the result without raising "collection modified."</para>
    /// </summary>
    public static IEnumerable<CacheKey> FindOverlapping(
        IReadOnlyCollection<CacheKey> haystack,
        byte unitId,
        ushort writeStart,
        ushort writeQty)
    {
        // writeQty = 0 — a degenerate write that covers no registers. Nothing to invalidate.
        if (writeQty == 0) return Array.Empty<CacheKey>();

        int writeEnd = writeStart + writeQty;   // half-open upper bound

        var hits = new List<CacheKey>();
        foreach (var key in haystack)
        {
            if (key.UnitId != unitId) continue;
            if (key.Fc != 0x03 && key.Fc != 0x04) continue;

            int keyEnd = key.StartAddress + key.Qty;
            // Overlap iff writeStart < keyEnd AND key.StartAddress < writeEnd.
            if (writeStart < keyEnd && key.StartAddress < writeEnd)
                hits.Add(key);
        }
        return hits;
    }
}
