// Configurable elements association
bool CConfigurableDomain::addConfigurableElement(CConfigurableElement *pConfigurableElement,
                                                 const CParameterBlackboard *pMainBlackboard,
                                                 core::Results &infos)
{
    // Already associated?
    if (containsConfigurableElement(pConfigurableElement)) {

        infos.push_back("Configurable element " + pConfigurableElement->getPath() +
                        " already associated to configuration domain " + getName());

        return false;
    }

    // Already owned?
    if (pConfigurableElement->belongsTo(this)) {

        infos.push_back("Configurable element " + pConfigurableElement->getPath() +
                        " already owned by configuration domain " + getName());

        return false;
    }

    // Do add
    doAddConfigurableElement(pConfigurableElement, infos, pMainBlackboard);

    return true;
}
// Domain splitting
bool CConfigurableDomain::split(CConfigurableElement *pConfigurableElement, core::Results &infos)
{
    // Not associated?
    if (!containsConfigurableElement(pConfigurableElement)) {

        std::string strError = "Configurable element " + pConfigurableElement->getPath() +
                               " not associated to configuration domain " + getName();
        infos.push_back(strError);

        return false;
    }

    // Create sub domain areas for all configurable element's children
    size_t uiNbConfigurableElementChildren = pConfigurableElement->getNbChildren();

    if (!uiNbConfigurableElementChildren) {

        std::string strError = "Configurable element " + pConfigurableElement->getPath() +
                               " has no children to split configurable domain to";
        infos.push_back(strError);

        return false;
    }

    for (size_t uiChild = 0; uiChild < uiNbConfigurableElementChildren; uiChild++) {

        CConfigurableElement *pChildConfigurableElement =
            static_cast<CConfigurableElement *>(pConfigurableElement->getChild(uiChild));

        doAddConfigurableElement(pChildConfigurableElement, infos);
    }

    // Delegate to configurations
    size_t uiNbConfigurations = getNbChildren();

    for (size_t uiChild = 0; uiChild < uiNbConfigurations; uiChild++) {

        CDomainConfiguration *pDomainConfiguration =
            static_cast<CDomainConfiguration *>(getChild(uiChild));

        pDomainConfiguration->split(pConfigurableElement);
    }

    // Remove given configurable element from this domain
    // Note: we shouldn't need to recompute the sync set in that case, as the splitted element
    // should include the syncers of its children elements
    doRemoveConfigurableElement(pConfigurableElement, false);

    return true;
}
bool CConfigurableDomain::restoreConfiguration(const string &configurationName,
                                               CParameterBlackboard *mainBlackboard, bool autoSync,
                                               core::Results &errors) const
{
    string error;

    const CDomainConfiguration *configuration = findConfiguration(configurationName, error);

    if (configuration == NULL) {

        errors.push_back(error);
        return false;
    }

    // Delegate
    bool bSuccess = configuration->restore(mainBlackboard, autoSync && _bSequenceAware, &errors);

    // Record last applied configuration
    _pLastAppliedConfiguration = configuration;

    // Synchronize
    if (autoSync && !_bSequenceAware) {

        bSuccess &= _syncerSet.sync(*mainBlackboard, false, &errors);
    }
    return bSuccess;
}
// Configuration application if required
void CConfigurableDomains::apply(CParameterBlackboard *pParameterBlackboard, CSyncerSet &syncerSet,
                                 bool bForce, core::Results &infos) const
{
    /// Delegate to domains

    // Start with domains that can be synchronized all at once (with passed syncer set)
    size_t uiNbConfigurableDomains = getNbChildren();

    for (size_t child = 0; child < uiNbConfigurableDomains; child++) {

        const CConfigurableDomain *pChildConfigurableDomain =
            static_cast<const CConfigurableDomain *>(getChild(child));

        std::string info;
        // Apply and collect syncers when relevant
        pChildConfigurableDomain->apply(pParameterBlackboard, &syncerSet, bForce, info);

        if (!info.empty()) {
            infos.push_back(info);
        }
    }
    // Synchronize those collected syncers
    syncerSet.sync(*pParameterBlackboard, false, nullptr);

    // Then deal with domains that need to synchronize along apply
    for (size_t child = 0; child < uiNbConfigurableDomains; child++) {

        const CConfigurableDomain *pChildConfigurableDomain =
            static_cast<const CConfigurableDomain *>(getChild(child));

        std::string info;
        // Apply and synchronize when relevant
        pChildConfigurableDomain->apply(pParameterBlackboard, nullptr, bForce, info);
        if (!info.empty()) {
            infos.push_back(info);
        }
    }
}
// Configurable element - domain association
bool CConfigurableDomains::addConfigurableElementToDomain(
    const string &domainName, CConfigurableElement *element,
    const CParameterBlackboard *mainBlackboard, core::Results &infos)
{
    // Find domain
    std::string error;
    CConfigurableDomain *domain = findConfigurableDomain(domainName, error);

    if (domain == nullptr) {

        infos.push_back(error);
        return false;
    }
    // Delegate
    return domain->addConfigurableElement(element, mainBlackboard, infos);
}
// Config restore
bool CConfigurableDomains::restoreConfiguration(const string &domainName,
                                                const string &configurationName,
                                                CParameterBlackboard *mainBlackboard, bool autoSync,
                                                core::Results &errors) const
{
    string error;
    // Find domain
    const CConfigurableDomain *domain = findConfigurableDomain(domainName, error);

    if (domain == nullptr) {

        errors.push_back(error);
        return false;
    }
    // Delegate
    return domain->restoreConfiguration(configurationName, mainBlackboard, autoSync, errors);
}
bool CConfigurableDomains::split(const string &domainName, CConfigurableElement *element,
                                 core::Results &infos)
{
    // Find domain
    std::string error;
    CConfigurableDomain *domain = findConfigurableDomain(domainName, error);

    if (domain == nullptr) {

        infos.push_back(error);
        return false;
    }
    // Delegate
    domain->split(element, infos);

    return true;
}
// Configurable elements association
void CConfigurableDomain::doAddConfigurableElement(CConfigurableElement *pConfigurableElement,
                                                   core::Results &infos,
                                                   const CParameterBlackboard *pMainBlackboard)
{
    // Inform configurable element
    pConfigurableElement->addAttachedConfigurableDomain(this);

    // Create associated syncer set
    CSyncerSet *pSyncerSet = new CSyncerSet;

    // Add to sync set the configurable element one
    pConfigurableElement->fillSyncerSet(*pSyncerSet);

    // Store it
    _configurableElementToSyncerSetMap[pConfigurableElement] = pSyncerSet;

    // Add it to global one
    _syncerSet += *pSyncerSet;

    // Inform configurations
    size_t uiNbConfigurations = getNbChildren();

    for (size_t uiChild = 0; uiChild < uiNbConfigurations; uiChild++) {

        CDomainConfiguration *pDomainConfiguration =
            static_cast<CDomainConfiguration *>(getChild(uiChild));

        pDomainConfiguration->addConfigurableElement(pConfigurableElement, pSyncerSet);
    }

    // Ensure area validity for that configurable element (if main blackboard provided)
    if (pMainBlackboard) {

        infos.push_back("Validating domain '" + getName() +
                        "' against main blackboard for configurable element '" +
                        pConfigurableElement->getPath() + "'");
        // Need to validate against main blackboard
        validateAreas(pConfigurableElement, pMainBlackboard);
    }

    // Already associated descendend configurable elements need a merge of their configuration data
    mergeAlreadyAssociatedDescendantConfigurableElements(pConfigurableElement, infos);

    // Add to list
    _configurableElementList.push_back(pConfigurableElement);
}
// Merge any descended configurable element to this one with this one
void CConfigurableDomain::mergeAlreadyAssociatedDescendantConfigurableElements(
    CConfigurableElement *newElement, core::Results &infos)
{
    std::list<CConfigurableElement *> mergedConfigurableElementList;

    ConfigurableElementListIterator it;

    // Browse all configurable elements (new one not yet in the list!)
    for (it = _configurableElementList.begin(); it != _configurableElementList.end(); ++it) {

        CConfigurableElement *pConfigurablePotentialDescendantElement = *it;

        if (pConfigurablePotentialDescendantElement->isDescendantOf(newElement)) {

            infos.push_back("In domain '" + getName() +
                            "', merging descendant configurable element's configurations '" +
                            pConfigurablePotentialDescendantElement->getName() +
                            "' into its ascendant '" + newElement->getName() + "' ones");

            // Merge configuration data
            mergeConfigurations(newElement, pConfigurablePotentialDescendantElement);

            // Keep track for removal
            mergedConfigurableElementList.push_back(pConfigurablePotentialDescendantElement);
        }
    }

    // Remove all merged elements (new one not yet in the list!)
    for (it = mergedConfigurableElementList.begin(); it != mergedConfigurableElementList.end();
         ++it) {

        CConfigurableElement *pMergedConfigurableElement = *it;

        // Remove merged from configurable element from internal tracking list
        // Note: we shouldn't need to recompute the sync set in that case, as the merged to element
        // should include the syncers of merged from elements
        doRemoveConfigurableElement(pMergedConfigurableElement, false);
    }
}