/**
 * Execute the MoveInstrumentComponent on all (named) subcomponents
 * @param toCorrect : Workspace to correct
 * @param detector : Detector or DetectorGroup
 * @param sample : Sample Component
 * @param upOffset : Up offset to apply
 * @param acrossOffset : Across offset to apply
 * @param detectorPosition: Actual detector or detector group position.
 */
void SpecularReflectionPositionCorrect::moveDetectors(
    API::MatrixWorkspace_sptr toCorrect, IComponent_const_sptr detector,
    IComponent_const_sptr sample, const double &upOffset,
    const double &acrossOffset, const V3D &detectorPosition) {
  auto instrument = toCorrect->getInstrument();
  const V3D samplePosition = sample->getPos();
  auto referenceFrame = instrument->getReferenceFrame();
  if (auto groupDetector = boost::dynamic_pointer_cast<const DetectorGroup>(
          detector)) // Do we have a group of detectors
  {
    const std::vector<IDetector_const_sptr> detectors =
        groupDetector->getDetectors();
    const bool commonParent = hasCommonParent(detectors);
    if (commonParent) {
      /*
       * Same parent component. So lets move that.
       */
      moveDetectors(toCorrect, detectors[0], sample, upOffset, acrossOffset,
                    detectorPosition); // Recursive call
    } else {
      /*
       * We have to move individual components.
       */
      for (size_t i = 0; i < detectors.size(); ++i) {
        moveDetectors(toCorrect, detectors[i], sample, upOffset, acrossOffset,
                      detectorPosition); // Recursive call
      }
    }
  } else {
    auto moveComponentAlg =
        this->createChildAlgorithm("MoveInstrumentComponent");
    moveComponentAlg->initialize();
    moveComponentAlg->setProperty("Workspace", toCorrect);
    IComponent_const_sptr root = getParentComponent(detector);
    const std::string componentName = root->getName();
    moveComponentAlg->setProperty("ComponentName", componentName);
    moveComponentAlg->setProperty("RelativePosition", false);
    // Movements
    moveComponentAlg->setProperty(
        referenceFrame->pointingAlongBeamAxis(),
        detectorPosition.scalar_prod(referenceFrame->vecPointingAlongBeam()));
    moveComponentAlg->setProperty(referenceFrame->pointingHorizontalAxis(),
                                  acrossOffset);
    const double detectorVerticalPosition =
        detectorPosition.scalar_prod(referenceFrame->vecPointingUp());
    const double rootVerticalPosition =
        root->getPos().scalar_prod(referenceFrame->vecPointingUp());

    const double dm = rootVerticalPosition - detectorVerticalPosition;
    moveComponentAlg->setProperty(
        referenceFrame->pointingUpAxis(),
        samplePosition.scalar_prod(referenceFrame->vecPointingUp()) + upOffset +
            dm);
    // Execute the movement.
    moveComponentAlg->execute();
  }
}
/** Execute the algorithm.
 */
void CreateChunkingFromInstrument::exec() {
  // get the instrument
  Instrument_const_sptr inst = this->getInstrument();

  // setup the output workspace
  ITableWorkspace_sptr strategy =
      WorkspaceFactory::Instance().createTable("TableWorkspace");
  strategy->addColumn("str", "BankName");
  this->setProperty("OutputWorkspace", strategy);

  // get the correct level of grouping
  string groupLevel = this->getPropertyValue(PARAM_CHUNK_BY);
  vector<string> groupNames =
      getGroupNames(this->getPropertyValue(PARAM_CHUNK_NAMES));
  if (groupLevel.compare("All") == 0) {
    return; // nothing to do
  } else if (inst->getName().compare("SNAP") == 0 &&
             groupLevel.compare("Group") == 0) {
    groupNames.clear();
    groupNames.push_back("East");
    groupNames.push_back("West");
  }

  // set up a progress bar with the "correct" number of steps
  int maxBankNum = this->getProperty(PARAM_MAX_BANK_NUM);
  Progress progress(this, .2, 1., maxBankNum);

  // search the instrument for the bank names
  int maxRecurseDepth = this->getProperty(PARAM_MAX_RECURSE);
  map<string, vector<string>> grouping;
  // cppcheck-suppress syntaxError
    PRAGMA_OMP(parallel for schedule(dynamic, 1) )
    for (int num = 0; num < maxBankNum; ++num) {
      PARALLEL_START_INTERUPT_REGION
      ostringstream mess;
      mess << "bank" << num;
      IComponent_const_sptr comp =
          inst->getComponentByName(mess.str(), maxRecurseDepth);
      PARALLEL_CRITICAL(grouping)
      if (comp) {
        // get the name of the correct parent
        string parent;
        if (groupNames.empty()) {
          parent = parentName(comp, groupLevel);
        } else {
          parent = parentName(comp, groupNames);
        }

        // add it to the correct chunk
        if (!parent.empty()) {
          if (grouping.count(parent) == 0)
            grouping[parent] = vector<string>();

          grouping[parent].push_back(comp->getName());
        }
      }
      progress.report();
      PARALLEL_END_INTERUPT_REGION
    }
    PARALLEL_CHECK_INTERUPT_REGION

    // check to see that something happened
    if (grouping.empty())
      throw std::runtime_error("Failed to find any banks in the instrument");

    // fill in the table workspace
    for (auto group = grouping.begin(); group != grouping.end(); ++group) {
      stringstream banks;
      for (auto bank = group->second.begin(); bank != group->second.end();
           ++bank)
        banks << (*bank) << ",";

      // remove the trailing comma
      string banksStr = banks.str();
      banksStr = banksStr.substr(0, banksStr.size() - 1);

      // add it to the table
      TableRow row = strategy->appendRow();
      row << banksStr;
    }
}