using Theriapolis.Core.Util;

namespace Theriapolis.Core.Rules.Combat;

/// <summary>
/// Tactical-tile line-of-sight via Bresenham. The caller supplies a
/// "blocked at (x, y)?" predicate so this helper stays free of a hard
/// dependency on TacticalChunk / WorldState — Phase 5 M4 tests use a flat
/// arena (always-clear); M5 plugs in the live tactical-tile sampler.
/// </summary>
public static class LineOfSight
{
    /// <summary>
    /// True if a straight line from <paramref name="from"/> to
    /// <paramref name="to"/> traverses only un-blocked tiles. Endpoints
    /// themselves are NOT consulted — only the intermediate tiles.
    /// </summary>
    public static bool HasLine(Vec2 from, Vec2 to, System.Func<int, int, bool> isBlockedAt)
    {
        int x0 = (int)System.Math.Floor(from.X);
        int y0 = (int)System.Math.Floor(from.Y);
        int x1 = (int)System.Math.Floor(to.X);
        int y1 = (int)System.Math.Floor(to.Y);

        int dx = System.Math.Abs(x1 - x0);
        int dy = System.Math.Abs(y1 - y0);
        int sx = x0 < x1 ? 1 : -1;
        int sy = y0 < y1 ? 1 : -1;
        int err = dx - dy;

        int x = x0, y = y0;
        while (true)
        {
            // Skip the endpoint itself
            if (!(x == x0 && y == y0) && !(x == x1 && y == y1))
            {
                if (isBlockedAt(x, y)) return false;
            }
            if (x == x1 && y == y1) return true;
            int e2 = 2 * err;
            if (e2 > -dy) { err -= dy; x  += sx; }
            if (e2 <  dx) { err += dx; y  += sy; }
        }
    }

    /// <summary>Convenience: always-clear arena. Used by combat-duel and most M4 tests.</summary>
    public static readonly System.Func<int, int, bool> AlwaysClear = (_, _) => false;
}
