/**
 * Determine the instrument from the various input parameters.
 *
 * @return The correct instrument.
 */
Instrument_const_sptr CreateChunkingFromInstrument::getInstrument() {
  // try the input workspace
  MatrixWorkspace_sptr inWS = getProperty(PARAM_IN_WKSP);
  if (inWS) {
    return inWS->getInstrument();
  }

  // temporary workspace to hang everything else off of
  MatrixWorkspace_sptr tempWS(new Workspace2D());
  // name of the instrument
  string instName = getPropertyValue(PARAM_INST_NAME);

  // see if there is an input file
  string filename = getPropertyValue(PARAM_IN_FILE);
  if (!filename.empty()) {
    string top_entry_name("entry"); // TODO make more flexible

    // get the instrument name from the filename
    size_t n = filename.rfind('/');
    if (n != std::string::npos) {
      std::string temp = filename.substr(n + 1, filename.size() - n - 1);
      n = temp.find('_');
      if (n != std::string::npos && n > 0) {
        instName = temp.substr(0, n);
      }
    }

    // read information from the nexus file itself
    try {
      NeXus::File nxsfile(filename);

      // get the run start time
      string start_time;
      nxsfile.openGroup(top_entry_name, "NXentry");
      nxsfile.readData("start_time", start_time);
      tempWS->mutableRun().addProperty(
          "run_start", DateAndTime(start_time).toISO8601String(), true);

      // get the instrument name
      nxsfile.openGroup("instrument", "NXinstrument");
      nxsfile.readData("name", instName);
      nxsfile.closeGroup();

      // Test if IDF exists in file, move on quickly if not
      nxsfile.openPath("instrument/instrument_xml");
      nxsfile.close();
      IAlgorithm_sptr loadInst =
          createChildAlgorithm("LoadIDFFromNexus", 0.0, 0.2);
      // Now execute the Child Algorithm. Catch and log any error, but don't
      // stop.
      try {
        loadInst->setPropertyValue("Filename", filename);
        loadInst->setProperty<MatrixWorkspace_sptr>("Workspace", tempWS);
        loadInst->setPropertyValue("InstrumentParentPath", top_entry_name);
        loadInst->execute();
      } catch (std::invalid_argument &) {
        g_log.error("Invalid argument to LoadIDFFromNexus Child Algorithm ");
      } catch (std::runtime_error &) {
        g_log.debug("No instrument definition found in " + filename + " at " +
                    top_entry_name + "/instrument");
      }

      if (loadInst->isExecuted())
        return tempWS->getInstrument();
      else
        g_log.information("No IDF loaded from Nexus file.");

    } catch (::NeXus::Exception &) {
      g_log.information("No instrument definition found in " + filename +
                        " at " + top_entry_name + "/instrument");
    }
  }

  // run LoadInstrument if other methods have not run
  string instFilename = getPropertyValue(PARAM_INST_FILE);

  Algorithm_sptr childAlg = createChildAlgorithm("LoadInstrument", 0.0, 0.2);
  childAlg->setProperty<MatrixWorkspace_sptr>("Workspace", tempWS);
  childAlg->setPropertyValue("Filename", instFilename);
  childAlg->setPropertyValue("InstrumentName", instName);
  childAlg->executeAsChildAlg();
  return tempWS->getInstrument();
}
/** Execute the algorithm.
 */
void CreateGroupingWorkspace::exec() {
  MatrixWorkspace_sptr inWS = getProperty("InputWorkspace");
  std::string InstrumentName = getPropertyValue("InstrumentName");
  std::string InstrumentFilename = getPropertyValue("InstrumentFilename");
  std::string OldCalFilename = getPropertyValue("OldCalFilename");
  std::string GroupNames = getPropertyValue("GroupNames");
  std::string grouping = getPropertyValue("GroupDetectorsBy");
  int numGroups = getProperty("FixedGroupCount");
  std::string componentName = getPropertyValue("ComponentName");

  // Some validation
  int numParams = 0;
  if (inWS)
    numParams++;
  if (!InstrumentName.empty())
    numParams++;
  if (!InstrumentFilename.empty())
    numParams++;

  if (numParams > 1)
    throw std::invalid_argument("You must specify exactly ONE way to get an "
                                "instrument (workspace, instrument name, or "
                                "IDF file). You specified more than one.");
  if (numParams == 0)
    throw std::invalid_argument("You must specify exactly ONE way to get an "
                                "instrument (workspace, instrument name, or "
                                "IDF file). You specified none.");

  if (!OldCalFilename.empty() && !GroupNames.empty())
    throw std::invalid_argument("You must specify either to use the "
                                "OldCalFilename parameter OR GroupNames but "
                                "not both!");

  bool sortnames = false;

  // ---------- Get the instrument one of 3 ways ---------------------------
  Instrument_const_sptr inst;
  if (inWS) {
    inst = inWS->getInstrument();
  } else {
    Algorithm_sptr childAlg = createChildAlgorithm("LoadInstrument", 0.0, 0.2);
    MatrixWorkspace_sptr tempWS(new Workspace2D());
    childAlg->setProperty<MatrixWorkspace_sptr>("Workspace", tempWS);
    childAlg->setPropertyValue("Filename", InstrumentFilename);
    childAlg->setProperty("RewriteSpectraMap",
                          Mantid::Kernel::OptionalBool(true));
    childAlg->setPropertyValue("InstrumentName", InstrumentName);
    childAlg->executeAsChildAlg();
    inst = tempWS->getInstrument();
  }

  if (GroupNames.empty() && OldCalFilename.empty()) {
    if (grouping.compare("All") == 0) {
      GroupNames = inst->getName();
    } else if (inst->getName().compare("SNAP") == 0 &&
               grouping.compare("Group") == 0) {
      GroupNames = "East,West";
    } else {
      sortnames = true;
      GroupNames = "";
      int maxRecurseDepth = this->getProperty("MaxRecursionDepth");

      // cppcheck-suppress syntaxError
          PRAGMA_OMP(parallel for schedule(dynamic, 1) )
          for (int num = 0; num < 300; ++num) {
            PARALLEL_START_INTERUPT_REGION
            std::ostringstream mess;
            mess << grouping << num;
            IComponent_const_sptr comp =
                inst->getComponentByName(mess.str(), maxRecurseDepth);
            PARALLEL_CRITICAL(GroupNames)
            if (comp)
              GroupNames += mess.str() + ",";
            PARALLEL_END_INTERUPT_REGION
          }
          PARALLEL_CHECK_INTERUPT_REGION
    }
  }

  // --------------------------- Create the output --------------------------
  GroupingWorkspace_sptr outWS(new GroupingWorkspace(inst));
  this->setProperty("OutputWorkspace", outWS);

  // This will get the grouping
  std::map<detid_t, int> detIDtoGroup;

  Progress prog(this, 0.2, 1.0, outWS->getNumberHistograms());
  // Make the grouping one of two ways:
  if (!GroupNames.empty())
    makeGroupingByNames(GroupNames, inst, detIDtoGroup, prog, sortnames);
  else if (!OldCalFilename.empty())
    readGroupingFile(OldCalFilename, detIDtoGroup, prog);
  else if ((numGroups > 0) && !componentName.empty())
    makeGroupingByNumGroups(componentName, numGroups, inst, detIDtoGroup, prog);

  g_log.information() << detIDtoGroup.size()
                      << " entries in the detectorID-to-group map.\n";
  setProperty("NumberGroupedSpectraResult",
              static_cast<int>(detIDtoGroup.size()));

  if (detIDtoGroup.empty()) {
    g_log.warning() << "Creating empty group workspace\n";
    setProperty("NumberGroupsResult", static_cast<int>(0));
  } else {
    size_t numNotFound = 0;

    // Make the groups, if any
    std::map<detid_t, int>::const_iterator it_end = detIDtoGroup.end();
    std::map<detid_t, int>::const_iterator it;
    std::set<int> groupCount;
    for (it = detIDtoGroup.begin(); it != it_end; ++it) {
      int detID = it->first;
      int group = it->second;
      groupCount.insert(group);
      try {
        outWS->setValue(detID, double(group));
      } catch (std::invalid_argument &) {
        numNotFound++;
      }
    }
    setProperty("NumberGroupsResult", static_cast<int>(groupCount.size()));

    if (numNotFound > 0)
      g_log.warning() << numNotFound << " detector IDs (out of "
                      << detIDtoGroup.size()
                      << ") were not found in the instrument\n.";
  }
}