namespace Theriapolis.Core.Util;

/// <summary>
/// Direction encoding for tile-level features (rivers, rail, roads).
/// Directions are stored as byte values 0–7, matching the 8 compass points.
/// 255 = "no direction" / unset.
/// </summary>
public static class Dir
{
    // Direction constants (0-based, CCW from North)
    public const byte None = 255;
    public const byte N    = 0;   // (dx= 0, dy=-1)
    public const byte NE   = 1;   // (dx= 1, dy=-1)
    public const byte E    = 2;   // (dx= 1, dy= 0)
    public const byte SE   = 3;   // (dx= 1, dy= 1)
    public const byte S    = 4;   // (dx= 0, dy= 1)
    public const byte SW   = 5;   // (dx=-1, dy= 1)
    public const byte W    = 6;   // (dx=-1, dy= 0)
    public const byte NW   = 7;   // (dx=-1, dy=-1)

    private static readonly (int dx, int dy)[] _deltas =
    {
        ( 0,-1), ( 1,-1), ( 1, 0), ( 1, 1),
        ( 0, 1), (-1, 1), (-1, 0), (-1,-1),
    };

    /// <summary>Convert a (dx, dy) delta to a direction byte. Both values must be in {-1,0,1}.</summary>
    public static byte FromDelta(int dx, int dy)
    {
        for (byte i = 0; i < 8; i++)
            if (_deltas[i].dx == dx && _deltas[i].dy == dy)
                return i;
        return None;
    }

    public static (int dx, int dy) ToDelta(byte dir)
    {
        if (dir == None) return (0, 0);
        return _deltas[dir & 7];
    }

    /// <summary>
    /// Two directions are parallel if their angular difference is ≤ 45° (including opposite directions).
    /// </summary>
    public static bool IsParallel(byte a, byte b)
    {
        if (a == None || b == None) return false;
        int diff = Math.Abs((int)(a & 7) - (int)(b & 7));
        diff = Math.Min(diff, 8 - diff);
        // diff 0 = same; diff 1 = 45°; diff 4 = 180° (opposite still parallel)
        return diff <= 1 || diff >= 3; // ≤45° or ≥135° (anti-parallel)
    }

    /// <summary>
    /// Two directions are perpendicular if their angular difference is in [60°, 120°].
    /// Using discrete 45° steps: diff 2 = 90°.
    /// </summary>
    public static bool IsPerpendicular(byte a, byte b)
    {
        if (a == None || b == None) return false;
        int diff = Math.Abs((int)(a & 7) - (int)(b & 7));
        diff = Math.Min(diff, 8 - diff);
        return diff == 2;
    }

    /// <summary>Opposite direction.</summary>
    public static byte Opposite(byte d) => d == None ? None : (byte)((d + 4) & 7);
}
