namespace Theriapolis.Core.Tactical;

/// <summary>
/// Integer (cx, cy) key for a tactical chunk. The chunk covers world-pixel
/// rectangle [cx*CHUNK_SIZE, cy*CHUNK_SIZE, (cx+1)*CHUNK_SIZE, (cy+1)*CHUNK_SIZE).
/// At the current constants (CHUNK_SIZE = 64, TACTICAL_PER_WORLD_TILE = 32),
/// each chunk covers 2×2 world tiles.
/// </summary>
public readonly struct ChunkCoord : IEquatable<ChunkCoord>
{
    public readonly int X;
    public readonly int Y;
    public ChunkCoord(int x, int y) { X = x; Y = y; }

    /// <summary>Chunk that contains the given tactical-tile (world-pixel) coordinate.</summary>
    public static ChunkCoord ForTactical(int tx, int ty)
        => new(FloorDiv(tx, C.TACTICAL_CHUNK_SIZE), FloorDiv(ty, C.TACTICAL_CHUNK_SIZE));

    /// <summary>Chunk that contains the given world-tile coordinate.</summary>
    public static ChunkCoord ForWorldTile(int wx, int wy)
    {
        // 1 world tile = TACTICAL_PER_WORLD_TILE world pixels = TACTICAL_PER_WORLD_TILE
        // tactical tiles. One chunk covers CHUNK_SIZE / TACTICAL_PER_WORLD_TILE world tiles.
        int worldTilesPerChunk = C.TACTICAL_CHUNK_SIZE / C.TACTICAL_PER_WORLD_TILE;
        return new(FloorDiv(wx, worldTilesPerChunk), FloorDiv(wy, worldTilesPerChunk));
    }

    private static int FloorDiv(int a, int b)
    {
        int q = a / b;
        if ((a ^ b) < 0 && q * b != a) q--;
        return q;
    }

    public bool Equals(ChunkCoord other) => X == other.X && Y == other.Y;
    public override bool Equals(object? obj) => obj is ChunkCoord c && Equals(c);
    public override int GetHashCode() => HashCode.Combine(X, Y);
    public static bool operator ==(ChunkCoord a, ChunkCoord b) => a.Equals(b);
    public static bool operator !=(ChunkCoord a, ChunkCoord b) => !a.Equals(b);
    public override string ToString() => $"({X},{Y})";
}
