text: //
  // (C) 2013 Opensim Ltd.
  //
  // This library is free software, you can redistribute it
  // and/or modify
  // it under  the terms of the GNU Lesser General Public License
  // as published by the Free Software Foundation;
  // either version 2 of the License, or any later version.
  // The library is distributed in the hope that it will be useful,
  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  // See the GNU Lesser General Public License for more details.
  //
  // Author: Andras Varga (andras@omnetpp.org)
  //
  
  #include <algorithm>
  #include "inet/common/lifecycle/LifecycleController.h"
  #include "inet/networklayer/contract/IInterfaceTable.h"
  #include "inet/networklayer/common/InterfaceEntry.h"
  #include "inet/networklayer/common/L3AddressResolver.h"
  #include "inet/common/lifecycle/LifecycleOperation.h"
  #include "inet/common/INETUtils.h"
  
  namespace inet {
  
  Define_Module(LifecycleController);
  
  void LifecycleController::Callback::init(LifecycleController *controller, LifecycleOperation *operation, cModule *module)
  {
      this->controller = controller;
      this->operation = operation;
      this->module = module;
  }
  
  void LifecycleController::Callback::invoke()
  {
      if (!controller)
          throw cRuntimeError("Usage error: callback may only be invoked if (and after) you returned 'false' from initiateStateChange()");
      controller->moduleOperationStageCompleted(this);
  }
  
  //----
  
  template<typename T>
  void vector_delete_element(std::vector<T *>& v, T *p)
  {
      auto it = std::find(v.begin(), v.end(), p);
      ASSERT(it != v.end());
      v.erase(it);
      delete p;
  }
  
  void LifecycleController::initialize()
  {
  }
  
  void LifecycleController::handleMessage(cMessage *msg)
  {
      throw cRuntimeError("This module does not process messages");
  }
  
  void LifecycleController::processCommand(const cXMLElement& node)
  {
      // resolve target module
      const char *target = node.getAttribute("target");
      cModule *module = getModuleByPath(target);
      if (!module)
          throw cRuntimeError("Module '%s' not found", target);
  
      // resolve operation
      const char *operationName = node.getAttribute("operation");
      LifecycleOperation *operation = check_and_cast<LifecycleOperation *>(inet::utils::createOne(operationName));
      std::map<std::string, std::string> params = node.getAttributes();
      params.erase("module");
      params.erase("t");
      params.erase("target");
      params.erase("operation");
      operation->initialize(module, params);
      if (!params.empty())
          throw cRuntimeError("Unknown parameter '%s' for operation %s at %s", params.begin()->first.c_str(), operationName, node.getSourceLocation());
  
      // do the operation
      initiateOperation(operation);
  }
  
  bool LifecycleController::initiateOperation(LifecycleOperation *operation, IDoneCallback *completionCallback)
  {
      Enter_Method_Silent();
      operation->currentStage = 0;
      operation->operationCompletionCallback = completionCallback;
      operation->insideInitiateOperation = true;
      return resumeOperation(operation);
  }
  
  bool LifecycleController::resumeOperation(LifecycleOperation *operation)
  {
      int numStages = operation->getNumStages();
      while (operation->currentStage < numStages) {
          EV << "Doing stage " << operation->currentStage << "/" << operation->getNumStages()
             << " of operation " << operation->getClassName() << " on " << operation->rootModule->getFullPath() << endl;
          doOneStage(operation, operation->rootModule);
          if (operation->pendingList.empty())
              operation->currentStage++;
          else
              return false; // pending
      }
  
      // done: invoke callback (unless we are still under initiateOperation())
      if (operation->operationCompletionCallback && !operation->insideInitiateOperation)
          operation->operationCompletionCallback->invoke();
      delete operation;
      return true;    // done
  }
  
  void LifecycleController::doOneStage(LifecycleOperation *operation, cModule *submodule)
  {
      ILifecycle *subject = dynamic_cast<ILifecycle *>(submodule);
      if (subject) {
          Callback *callback = spareCallback ? spareCallback : new Callback();
          bool done = subject->handleOperationStage(operation, operation->currentStage, callback);
          if (!done) {
              callback->init(this, operation, submodule);
              operation->pendingList.push_back(callback);
              spareCallback = nullptr;
          }
          else
              spareCallback = callback;
      }
  
      for (cModule::SubmoduleIterator i(submodule); !i.end(); i++) {
          cModule *child = i();
          doOneStage(operation, child);
      }
  }
  
  void LifecycleController::moduleOperationStageCompleted(Callback *callback)
  {
      Enter_Method_Silent();
  
      LifecycleOperation *operation = callback->operation;
      std::string moduleFullPath = callback->module->getFullPath();
      vector_delete_element(operation->pendingList, (IDoneCallback *)callback);
  
      EV << "Module " << moduleFullPath << " completed stage "
         << operation->currentStage << " of operation " << operation->getClassName() << ", "
         << operation->pendingList.size() << " more module(s) pending"
         << (operation->pendingList.empty() ? ", stage completed" : "") << endl;
  
      if (operation->pendingList.empty()) {
          operation->currentStage++;
          operation->insideInitiateOperation = false;
          resumeOperation(operation);
      }
  }
  
  } // namespace inet
  
  