/*
 * Inscoper NBO Example: Explore the Object Hierarchy
 * ==================================================
 * This example 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 include directory of the release must be on the include
 *    paths, and the application must link against the NBO library.
 */

// --8<-- [start:main_logic]
#include <NBO/NBO/Camera_Type.h>
#include <NBO/NBO/Detector_Type.h>
#include <NBO/NBO/Laser.h>
#include <NBO/NBO/MicroscopeStand_Type.h>
#include <NBO/Shared/Base/NBOBaseClass.h>

#include <iomanip>
#include <iostream>
#include <vector>

using namespace Inscoper;

// 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.
void printSubstitutions(const NBO::NBOBaseClass &element, int depth = 0) {
    for (const auto &child : element.getChildren()) {
        std::cout << std::string(4 * depth, ' ') << child->getXMLName() << std::endl;
        printSubstitutions(*child, depth + 1);
    }
}

int main() {
    // 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.
    NBO::Detector_Type detector;

    for (const auto &child : detector.getChildren()) {
        std::cout << std::left << std::setw(20) << child->getXMLName() << child->getTypeName()
                  << std::endl;
    }

    // A camera is itself a family of concrete detectors
    NBO::Camera_Type camera;
    for (const auto &child : camera.getChildren()) {
        std::cout << child->getXMLName() << " ";
    }
    std::cout << std::endl;

    // Step 2: List the elements a given type can contain
    // getSubElements() returns one instance per child element declared for the type.
    NBO::Laser laser;

    for (const auto &subElement : laser.getSubElements()) {
        std::cout << std::left << std::setw(30) << subElement->getXMLName()
                  << subElement->getTypeName() << std::endl;
    }

    NBO::MicroscopeStand_Type stand;
    printSubstitutions(stand);

    return 0;
}
// --8<-- [end:main_logic]
