"""
Inscoper NBO Example Script: Explore the Object Hierarchy
=========================================================
This script demonstrates how to discover, at run time, which concrete types can
substitute an abstract NBO element and which elements a given type can contain.

Prerequisites:
-------------
1. Installation: The `inscoper_nbo` wheel must be installed in the active Python
   environment.
"""

# --8<-- [start:main_logic]
import inscoper_nbo

# ── Step 1: List the concrete types that may substitute an abstract element
# Detector_Type is abstract in the schema. getChildren() returns one instance per
# admissible substitution, from which the XML name and the type name are read.
detector = inscoper_nbo.Detector_Type()

for child in detector.getChildren():
    print(f"{child.getXMLName():<20} {child.getTypeName()}")

# A camera is itself a family of concrete detectors
camera = inscoper_nbo.Camera_Type()
print([child.getXMLName() for child in camera.getChildren()])

# ── Step 2: List the elements a given type can contain
# getSubElements() returns one instance per child element declared for the type.
laser = inscoper_nbo.Laser()

for sub_element in laser.getSubElements():
    print(f"{sub_element.getXMLName():<30} {sub_element.getTypeName()}")

# ── Step 3: Walk the hierarchy recursively
# Both accessors return NBO objects, so the same call can be applied again to
# each result to explore an arbitrary depth of the schema.
def print_substitutions(element, depth=0):
    for child in element.getChildren():
        print(f"{'    ' * depth}{child.getXMLName()}")
        print_substitutions(child, depth + 1)

print_substitutions(inscoper_nbo.MicroscopeStand_Type())
# --8<-- [end:main_logic]
