#!/usr/bin/env python3
"""Test for issue #176: Entity position naming consistency.

Tests the new Entity position properties:
- pos, x, y: pixel coordinates (requires grid attachment)
- grid_pos, grid_x, grid_y: integer tile coordinates
- draw_pos: fractional tile coordinates for animation
"""
import mcrfpy
import sys

def test_entity_positions():
    """Test Entity position properties with grid attachment."""
    errors = []

    # Create a texture with 16x16 sprites (standard tile size)
    texture = mcrfpy.Texture("assets/kenney_tinydungeon.png", 16, 16)

    # Create a grid (10x10 tiles, 16x16 pixels each)
    grid = mcrfpy.Grid(grid_size=(10, 10), texture=texture, pos=(0, 0), size=(160, 160))

    # Create entity at tile position (3, 5)
    entity = mcrfpy.Entity(grid_pos=(3, 5), texture=texture, grid=grid)

    # Test 1: grid_pos should return integer tile coordinates
    gpos = entity.grid_pos
    if gpos.x != 3 or gpos.y != 5:
        errors.append(f"grid_pos: expected (3, 5), got ({gpos.x}, {gpos.y})")

    # Test 2: grid_x and grid_y should return integers
    if entity.grid_x != 3:
        errors.append(f"grid_x: expected 3, got {entity.grid_x}")
    if entity.grid_y != 5:
        errors.append(f"grid_y: expected 5, got {entity.grid_y}")

    # Test 3: draw_pos should return float tile coordinates
    dpos = entity.draw_pos
    if abs(dpos.x - 3.0) > 0.001 or abs(dpos.y - 5.0) > 0.001:
        errors.append(f"draw_pos: expected (3.0, 5.0), got ({dpos.x}, {dpos.y})")

    # Test 4: pos should return pixel coordinates (tile * tile_size)
    # With 16x16 tiles: (3, 5) tiles = (48, 80) pixels
    ppos = entity.pos
    if abs(ppos.x - 48.0) > 0.001 or abs(ppos.y - 80.0) > 0.001:
        errors.append(f"pos: expected (48.0, 80.0), got ({ppos.x}, {ppos.y})")

    # Test 5: x and y should return pixel coordinates
    if abs(entity.x - 48.0) > 0.001:
        errors.append(f"x: expected 48.0, got {entity.x}")
    if abs(entity.y - 80.0) > 0.001:
        errors.append(f"y: expected 80.0, got {entity.y}")

    # Test 6: Setting grid_x/grid_y should update cell position (#295: decoupled from pixel pos)
    entity.grid_x = 7
    entity.grid_y = 2
    if entity.grid_x != 7 or entity.grid_y != 2:
        errors.append(f"After setting grid_x/y: expected (7, 2), got ({entity.grid_x}, {entity.grid_y})")
    # #295: cell_pos (grid_x/y) is decoupled from pixel pos - pixel pos NOT updated
    # Pixel pos should remain at the draw_pos * tile_size (3*16=48, 5*16=80 from earlier)
    if abs(entity.x - 48.0) > 0.001 or abs(entity.y - 80.0) > 0.001:
        errors.append(f"After grid_x/y set, pixel pos should be unchanged: expected (48, 80), got ({entity.x}, {entity.y})")

    # Test 7: Setting pos (pixels) should update draw_pos but NOT grid_pos (#295)
    entity.pos = mcrfpy.Vector(64, 96)  # (64, 96) / 16 = (4, 6) tiles
    if abs(entity.draw_pos.x - 4.0) > 0.001 or abs(entity.draw_pos.y - 6.0) > 0.001:
        errors.append(f"After setting pos, draw_pos: expected (4, 6), got ({entity.draw_pos.x}, {entity.draw_pos.y})")
    # #295: grid_pos is cell_pos, not derived from float position - should be (7, 2) from above
    if entity.grid_x != 7 or entity.grid_y != 2:
        errors.append(f"After setting pos, grid_x/y should be unchanged: expected (7, 2), got ({entity.grid_x}, {entity.grid_y})")

    # Test 8: repr should show position info
    repr_str = repr(entity)
    if "draw_pos=" not in repr_str:
        errors.append(f"repr should contain draw_pos: {repr_str}")

    return errors


def test_entity_without_grid():
    """Test that pixel positions require grid attachment."""
    errors = []

    texture = mcrfpy.Texture("assets/kenney_tinydungeon.png", 16, 16)
    entity = mcrfpy.Entity(grid_pos=(3, 5), texture=texture)  # No grid

    # grid_pos should work without grid
    if entity.grid_x != 3 or entity.grid_y != 5:
        errors.append(f"grid_x/y without grid: expected (3, 5), got ({entity.grid_x}, {entity.grid_y})")

    # pos should raise RuntimeError without grid
    try:
        _ = entity.pos
        errors.append("entity.pos should raise RuntimeError without grid")
    except RuntimeError as e:
        if "not attached to a Grid" not in str(e):
            errors.append(f"Wrong error message for pos: {e}")

    # x should raise RuntimeError without grid
    try:
        _ = entity.x
        errors.append("entity.x should raise RuntimeError without grid")
    except RuntimeError as e:
        if "not attached to a Grid" not in str(e):
            errors.append(f"Wrong error message for x: {e}")

    # Setting pos should raise RuntimeError without grid
    try:
        entity.pos = mcrfpy.Vector(100, 100)
        errors.append("setting entity.pos should raise RuntimeError without grid")
    except RuntimeError as e:
        if "not attached to a Grid" not in str(e):
            errors.append(f"Wrong error message for setting pos: {e}")

    return errors


def test_animation_properties():
    """Test that animation properties work correctly."""
    errors = []

    texture = mcrfpy.Texture("assets/kenney_tinydungeon.png", 16, 16)
    grid = mcrfpy.Grid(grid_size=(10, 10), texture=texture, pos=(0, 0), size=(160, 160))
    entity = mcrfpy.Entity(grid_pos=(0, 0), texture=texture, grid=grid)

    # Test draw_x/draw_y animation properties exist
    try:
        # hasProperty should accept draw_x and draw_y
        # We can't call hasProperty directly, but we can try to animate
        # and check if it raises ValueError for invalid property
        pass  # Animation tested implicitly through animate() error handling
    except Exception as e:
        errors.append(f"Animation property test failed: {e}")

    return errors


def main():
    print("Testing issue #176: Entity position naming consistency")
    print("=" * 60)

    all_errors = []

    # Test 1: Entity with grid
    print("\n1. Testing entity positions with grid attachment...")
    errors = test_entity_positions()
    if errors:
        for e in errors:
            print(f"   FAIL: {e}")
        all_errors.extend(errors)
    else:
        print("   PASS")

    # Test 2: Entity without grid
    print("\n2. Testing entity positions without grid...")
    errors = test_entity_without_grid()
    if errors:
        for e in errors:
            print(f"   FAIL: {e}")
        all_errors.extend(errors)
    else:
        print("   PASS")

    # Test 3: Animation properties
    print("\n3. Testing animation properties...")
    errors = test_animation_properties()
    if errors:
        for e in errors:
            print(f"   FAIL: {e}")
        all_errors.extend(errors)
    else:
        print("   PASS")

    print("\n" + "=" * 60)
    if all_errors:
        print(f"FAILED: {len(all_errors)} error(s)")
        sys.exit(1)
    else:
        print("All tests passed!")
        sys.exit(0)


if __name__ == "__main__":
    main()
