from panda3d.core import Event, EventQueue
from contextlib import contextmanager
import gc


@contextmanager
def gc_disabled():
    gc.disable()
    gc.collect()
    gc.freeze()
    gc.set_debug(gc.DEBUG_SAVEALL)

    try:
        yield
    finally:
        gc.set_debug(0)
        gc.garbage.clear()
        gc.unfreeze()
        gc.collect()
        gc.enable()


def test_event_subclass_gc():
    class PythonEvent(Event):
        pass

    # Only subclasses should be tracked by the GC
    assert not gc.is_tracked(Event('test'))

    # Python wrapper destructs last
    with gc_disabled():
        event = PythonEvent('test_event_subclass_gc1')

        assert len(gc.get_objects()) == 1
        assert gc.get_objects()[0] == event

        event = None

        gc.collect()
        assert len(gc.garbage) == 1
        assert gc.garbage[0].name == 'test_event_subclass_gc1'

    # C++ reference destructs last
    with gc_disabled():
        event = PythonEvent('test_event_subclass_gc2')
        queue = EventQueue()
        queue.queue_event(event)

        assert event in gc.get_objects()

        event = None

        # Hasn't been collected yet, since parent still holds a reference
        gc.collect()
        assert len(gc.garbage) == 0
        assert 'test_event_subclass_gc2' in [obj.name for obj in gc.get_objects() if isinstance(obj, Event)]

        queue = None

        # Destructed without needing the GC
        assert 'test_event_subclass_gc2' not in [obj.name for obj in gc.get_objects() if isinstance(obj, Event)]

        gc.collect()
        assert len(gc.garbage) == 0
