#!/usr/bin/env python3
"""Test the object-oriented Scene API (alternative to module-level functions).

The Scene object provides an OOP approach to scene management with several advantages:
1. `scene.on_key` can be set on ANY scene, not just the current one
2. `scene.children` provides direct access to UI elements
3. Subclassing enables lifecycle callbacks (on_enter, on_exit, update, etc.)

This is the recommended approach for new code, replacing:
- mcrfpy.createScene(name) -> scene = mcrfpy.Scene(name)
- mcrfpy.setScene(name) -> scene.activate()
- mcrfpy.sceneUI(name) -> scene.children
- mcrfpy.keypressScene(callback) -> scene.on_key = callback
"""
import mcrfpy
import sys

def test_scene_object_basics():
    """Test basic Scene object creation and properties."""
    print("=== Test: Scene Object Basics ===")

    # Create scene using object-oriented approach
    scene = mcrfpy.Scene("oop_test")

    # Check name property
    assert scene.name == "oop_test", f"Expected 'oop_test', got '{scene.name}'"
    print(f"  name: {scene.name}")

    # Check active property (should be False, not yet activated)
    print(f"  active: {scene.active}")

    # Check children property returns UICollection
    children = scene.children
    print(f"  children type: {type(children).__name__}")
    print(f"  children count: {len(children)}")

    # Add UI elements via children
    frame = mcrfpy.Frame(pos=(50, 50), size=(200, 100), fill_color=mcrfpy.Color(100, 100, 200))
    scene.children.append(frame)
    print(f"  children count after append: {len(scene.children)}")

    print("  PASS: Basic properties work correctly")
    return scene

def test_scene_activation():
    """Test scene activation."""
    print("\n=== Test: Scene Activation ===")

    scene1 = mcrfpy.Scene("scene_a")
    scene2 = mcrfpy.Scene("scene_b")

    # Neither active yet
    print(f"  Before activation - scene1.active: {scene1.active}, scene2.active: {scene2.active}")

    # Activate scene1
    scene1.activate()
    print(f"  After scene1.activate() - scene1.active: {scene1.active}, scene2.active: {scene2.active}")
    assert scene1.active == True, "scene1 should be active"
    assert scene2.active == False, "scene2 should not be active"

    # Activate scene2
    scene2.activate()
    print(f"  After scene2.activate() - scene1.active: {scene1.active}, scene2.active: {scene2.active}")
    assert scene1.active == False, "scene1 should not be active now"
    assert scene2.active == True, "scene2 should be active"

    print("  PASS: Scene activation works correctly")

def test_scene_on_key():
    """Test setting on_key callback on scene objects.

    This is the KEY ADVANTAGE over module-level keypressScene():
    You can set on_key on ANY scene, not just the current one!
    """
    print("\n=== Test: Scene on_key Property ===")

    scene1 = mcrfpy.Scene("keys_scene1")
    scene2 = mcrfpy.Scene("keys_scene2")

    # Track which callback was called
    callback_log = []

    def scene1_keyhandler(key, action):
        callback_log.append(("scene1", key, action))

    def scene2_keyhandler(key, action):
        callback_log.append(("scene2", key, action))

    # Set callbacks on BOTH scenes BEFORE activating either
    # This is impossible with keypressScene() which only works on current scene!
    scene1.on_key = scene1_keyhandler
    scene2.on_key = scene2_keyhandler

    print(f"  scene1.on_key set: {scene1.on_key is not None}")
    print(f"  scene2.on_key set: {scene2.on_key is not None}")

    # Verify callbacks are retrievable
    assert callable(scene1.on_key), "scene1.on_key should be callable"
    assert callable(scene2.on_key), "scene2.on_key should be callable"

    # Test clearing callback
    scene1.on_key = None
    assert scene1.on_key is None, "scene1.on_key should be None after clearing"
    print("  scene1.on_key cleared successfully")

    # Re-set it
    scene1.on_key = scene1_keyhandler

    print("  PASS: on_key property works correctly")

def test_scene_visual_properties():
    """Test scene-level visual properties (pos, visible, opacity)."""
    print("\n=== Test: Scene Visual Properties ===")

    scene = mcrfpy.Scene("visual_props_test")

    # Test pos property
    print(f"  Initial pos: {scene.pos}")
    scene.pos = (100, 50)
    print(f"  After setting pos=(100, 50): {scene.pos}")

    # Test visible property
    print(f"  Initial visible: {scene.visible}")
    scene.visible = False
    print(f"  After setting visible=False: {scene.visible}")
    assert scene.visible == False, "visible should be False"
    scene.visible = True

    # Test opacity property
    print(f"  Initial opacity: {scene.opacity}")
    scene.opacity = 0.5
    print(f"  After setting opacity=0.5: {scene.opacity}")
    assert 0.49 < scene.opacity < 0.51, f"opacity should be ~0.5, got {scene.opacity}"

    print("  PASS: Visual properties work correctly")

def test_scene_subclass():
    """Test subclassing Scene for lifecycle callbacks."""
    print("\n=== Test: Scene Subclass with Lifecycle ===")

    class GameScene(mcrfpy.Scene):
        def __init__(self, name):
            super().__init__(name)
            self.enter_count = 0
            self.exit_count = 0
            self.update_count = 0

        def on_enter(self):
            self.enter_count += 1
            print(f"    GameScene.on_enter() called (count: {self.enter_count})")

        def on_exit(self):
            self.exit_count += 1
            print(f"    GameScene.on_exit() called (count: {self.exit_count})")

        def on_key(self, key, action):
            print(f"    GameScene.on_key({key}, {action})")

        def update(self, dt):
            self.update_count += 1
            # Note: update is called every frame, so we don't print

    game_scene = GameScene("game_scene_test")
    other_scene = mcrfpy.Scene("other_scene_test")

    # Add some UI to game scene
    game_scene.children.append(
        mcrfpy.Caption(pos=(100, 100), text="Game Scene", fill_color=mcrfpy.Color(255, 255, 255))
    )

    print(f"  Created GameScene with {len(game_scene.children)} children")
    print(f"  enter_count before activation: {game_scene.enter_count}")

    # Activate - should trigger on_enter
    game_scene.activate()
    print(f"  enter_count after activation: {game_scene.enter_count}")

    # Switch away - should trigger on_exit
    other_scene.activate()
    print(f"  exit_count after switching: {game_scene.exit_count}")

    print("  PASS: Subclassing works correctly")

def test_comparison_with_module_functions():
    """Demonstrate the difference between old and new approaches."""
    print("\n=== Comparison: Module Functions vs Scene Objects ===")

    print("\n  OLD APPROACH (module-level functions):")
    print("    mcrfpy.createScene('my_scene')")
    print("    mcrfpy.setScene('my_scene')")
    print("    ui = mcrfpy.sceneUI('my_scene')")
    print("    ui.append(mcrfpy.Frame(...))")
    print("    mcrfpy.keypressScene(handler)  # ONLY works on current scene!")

    print("\n  NEW APPROACH (Scene objects):")
    print("    scene = mcrfpy.Scene('my_scene')")
    print("    scene.activate()")
    print("    scene.children.append(mcrfpy.Frame(...))")
    print("    scene.on_key = handler  # Works on ANY scene!")

    print("\n  KEY BENEFITS:")
    print("    1. scene.on_key can be set on non-active scenes")
    print("    2. Subclassing enables on_enter/on_exit/update callbacks")
    print("    3. Object reference makes code more readable")
    print("    4. scene.children is clearer than sceneUI(name)")

    print("\n  PASS: Documentation complete")

def main():
    """Run all Scene object API tests."""
    print("=" * 60)
    print("Scene Object API Test Suite")
    print("=" * 60)

    try:
        test_scene_object_basics()
        test_scene_activation()
        test_scene_on_key()
        test_scene_visual_properties()
        test_scene_subclass()
        test_comparison_with_module_functions()

        print("\n" + "=" * 60)
        print("ALL TESTS PASSED!")
        print("=" * 60)
        sys.exit(0)

    except Exception as e:
        print(f"\nTEST FAILED: {e}")
        import traceback
        traceback.print_exc()
        sys.exit(1)

if __name__ == "__main__":
    main()
