/*
Extract the geometry and function information

This implementation is an override of the base-class method, which deals with
the more common event based route. However the SQW files will provide complete
dimensions with ranges already set. Less work needs to be done here than for
event workspaces where the extents of each dimension need to be individually
extracted.

@param eventWs : event workspace to get the information from.
*/
void SQWLoadingPresenter::extractMetadata(
    const Mantid::API::IMDEventWorkspace &eventWs) {
  using namespace Mantid::Geometry;
  MDGeometryBuilderXML<NoDimensionPolicy> refresh;
  this->xmlBuilder = refresh; // Reassign.
  std::vector<IMDDimension_sptr> dimensions;
  size_t nDimensions = eventWs.getNumDims();
  for (size_t d = 0; d < nDimensions; d++) {
    IMDDimension_const_sptr inDim = eventWs.getDimension(d);
    axisLabels.push_back(makeAxisTitle(*inDim));
    // Copy the dimension, but set the ID and name to be the same. This is an
    // assumption in bintohistoworkspace.
    dimensions.push_back(boost::make_shared<MDHistoDimension>(
        inDim->getName(), inDim->getName(), inDim->getMDFrame(),
        inDim->getMinimum(), inDim->getMaximum(), size_t{10}));
  }

  // Configuring the geometry xml builder allows the object panel associated
  // with this reader to later
  // determine how to display all geometry related properties.
  if (nDimensions > 0) {
    this->xmlBuilder.addXDimension(dimensions[0]);
  }
  if (nDimensions > 1) {
    this->xmlBuilder.addYDimension(dimensions[1]);
  }
  if (nDimensions > 2) {
    this->xmlBuilder.addZDimension(dimensions[2]);
  }
  if (nDimensions > 3) {
    this->tDimension = dimensions[3];
    this->xmlBuilder.addTDimension(this->tDimension);
  }
  this->m_isSetup = true;
}
//-----------------------------------------------------------------------------
/// @return the label for the X axis
QString MantidQwtIMDWorkspaceData::getXAxisLabel() const
{
  QString xLabel;
  if ( m_originalWorkspace.expired() )
    return xLabel; // Empty string
  if (m_currentPlotAxis >= 0)
  {
    // One of the dimensions of the original
    IMDDimension_const_sptr dim = m_originalWorkspace.lock()->getDimension(m_currentPlotAxis);
    xLabel = QString::fromStdString(dim->getName()) + " (" + QString::fromStdWString(dim->getUnits().utf8()) + ")";
  }
  else
  {
    // Distance
    // Distance, or not set.
    xLabel = "Distance from start";
//    if (dimX->getUnits() == dimY->getUnits())
//      xLabel += " (" + dimX->getUnits() + ")";
//    else
//      xLabel += " (undefined units)";
//    break;
  }

  return xLabel;
}
     /*
    Extract the geometry and function information 
    @param eventWs : event workspace to get the information from.
    */
    void MDEWLoadingPresenter::extractMetadata(Mantid::API::IMDEventWorkspace_sptr eventWs)
    {
      using namespace Mantid::Geometry;
      MDGeometryBuilderXML<NoDimensionPolicy> refresh;
      xmlBuilder= refresh; //Reassign.
      std::vector<MDDimensionExtents<coord_t> > ext = eventWs->getMinimumExtents(5);
      std::vector<IMDDimension_sptr> dimensions;
      size_t nDimensions = eventWs->getNumDims();
      for (size_t d=0; d<nDimensions; d++)
      {
        IMDDimension_const_sptr inDim = eventWs->getDimension(d);
        coord_t min = ext[d].getMin();
        coord_t max = ext[d].getMax();
        if (min > max)
        {
          min = 0.0;
          max = 1.0;
        }
        //std::cout << "dim " << d << min << " to " <<  max << std::endl;
        axisLabels.push_back(makeAxisTitle(inDim));
        MDHistoDimension_sptr dim(new MDHistoDimension(inDim->getName(), inDim->getName(), inDim->getUnits(), min, max, inDim->getNBins()));
        dimensions.push_back(dim);
      }

      //Configuring the geometry xml builder allows the object panel associated with this reader to later
      //determine how to display all geometry related properties.
      if(nDimensions > 0)
      {
        xmlBuilder.addXDimension( dimensions[0] );
      }
      if(nDimensions > 1)
      {
        xmlBuilder.addYDimension( dimensions[1] );
      }
      if(nDimensions > 2)
      {
        xmlBuilder.addZDimension( dimensions[2]  );
      }
      if(nDimensions > 3)
      {
        tDimension = dimensions[3];
        xmlBuilder.addTDimension(tDimension);
      }
      m_isSetup = true;
    }
/** Set the original workspace, to show the axes plot choice */
void LinePlotOptions::setOriginalWorkspace(Mantid::API::IMDWorkspace_sptr ws) {
  if (!ws)
    return;

  for (size_t d = 0; d < (ws->getNumDims()); d++) {
    IMDDimension_const_sptr dim = ws->getDimension(d);
    std::string text = dim->getName();
    std::string tooltip =
        "Use the " + dim->getName() + " dimension as the X plot axis.";
    const bool bIntegrated = dim->getIsIntegrated();
    // Index into the radio buttons array
    int index = int(d) + 2;
    if (m_radPlots.size() > index) {
      m_radPlots[index]->setText(QString::fromStdString(text));
      m_radPlots[index]->setToolTip(QString::fromStdString(tooltip));
    } else
      addPlotRadioButton(text, tooltip, bIntegrated);
  }
}
/*
Extract the geometry and function information
@param histoWs : histogram workspace to get the information from.
*/
void MDHWLoadingPresenter::extractMetadata(
    const Mantid::API::IMDHistoWorkspace &histoWs) {
  using namespace Mantid::Geometry;
  MDGeometryBuilderXML<NoDimensionPolicy> refresh;
  xmlBuilder = refresh; // Reassign.
  std::vector<IMDDimension_sptr> dimensions;
  size_t nDimensions = histoWs.getNumDims();
  for (size_t d = 0; d < nDimensions; d++) {
    IMDDimension_const_sptr inDim = histoWs.getDimension(d);
    coord_t min = inDim->getMinimum();
    coord_t max = inDim->getMaximum();
    if (min > max) {
      min = 0.0;
      max = 1.0;
    }
    // std::cout << "dim " << d << min << " to " <<  max << '\n';
    axisLabels.push_back(makeAxisTitle(*inDim));
    dimensions.push_back(boost::make_shared<MDHistoDimension>(
        inDim->getName(), inDim->getName(), inDim->getMDFrame(), min, max,
        inDim->getNBins()));
  }

  // Configuring the geometry xml builder allows the object panel associated
  // with this reader to later
  // determine how to display all geometry related properties.
  if (nDimensions > 0) {
    xmlBuilder.addXDimension(dimensions[0]);
  }
  if (nDimensions > 1) {
    xmlBuilder.addYDimension(dimensions[1]);
  }
  if (nDimensions > 2) {
    xmlBuilder.addZDimension(dimensions[2]);
  }
  if (nDimensions > 3) {
    tDimension = dimensions[3];
    xmlBuilder.addTDimension(tDimension);
  }
  m_isSetup = true;
}
/** Execute the algorithm.
 */
void BinaryOperationMD::exec() {
  // Get the properties
  m_lhs = getProperty(inputPropName1());
  m_rhs = getProperty(inputPropName2());
  m_out = getProperty(outputPropName());

  // Flip LHS and RHS if commutative and :
  //  1. A = B + A -> becomes -> A += B
  //  1. C = 1 + A -> becomes -> C = A + 1   (number is always on RHS if
  //  possible)
  if (this->commutative() &&
      ((m_out == m_rhs) ||
       boost::dynamic_pointer_cast<WorkspaceSingleValue>(m_lhs))) {
    // So we flip RHS/LHS
    Mantid::API::IMDWorkspace_sptr temp = m_lhs;
    m_lhs = m_rhs;
    m_rhs = temp;
  }

  // Do not compare conventions if one is single value
  if (!boost::dynamic_pointer_cast<WorkspaceSingleValue>(m_rhs)) {
    if (m_lhs->getConvention() != m_rhs->getConvention()) {
      throw std::runtime_error(
          "Workspaces have different conventions for Q. "
          "Use algorithm ChangeQConvention on one workspace. ");
    }
  }

  // Can't do A = 1 / B
  if (boost::dynamic_pointer_cast<MatrixWorkspace>(m_lhs))
    throw std::invalid_argument("BinaryOperationMD: can't have a "
                                "MatrixWorkspace (e.g. WorkspaceSingleValue) "
                                "as the LHS argument of " +
                                this->name() + ".");

  // Check the inputs. First cast to everything
  m_lhs_event = boost::dynamic_pointer_cast<IMDEventWorkspace>(m_lhs);
  m_lhs_histo = boost::dynamic_pointer_cast<MDHistoWorkspace>(m_lhs);
  m_lhs_scalar = boost::dynamic_pointer_cast<WorkspaceSingleValue>(m_lhs);
  m_rhs_event = boost::dynamic_pointer_cast<IMDEventWorkspace>(m_rhs);
  m_rhs_histo = boost::dynamic_pointer_cast<MDHistoWorkspace>(m_rhs);
  m_rhs_scalar = boost::dynamic_pointer_cast<WorkspaceSingleValue>(m_rhs);

  // MDEventWorkspaces only:
  // If you have to clone any WS, and the operation is commutative, and is NOT
  // in-place, then clone the one that is file-backed.
  if (this->commutative() && (m_lhs_event && m_rhs_event) && (m_out != m_lhs)) {
    if (m_rhs_event->isFileBacked() && !m_lhs_event->isFileBacked()) {
      // So we flip RHS/LHS
      Mantid::API::IMDWorkspace_sptr temp = m_lhs;
      m_lhs = m_rhs;
      m_rhs = temp;
      m_lhs_event = boost::dynamic_pointer_cast<IMDEventWorkspace>(m_lhs);
      m_rhs_event = boost::dynamic_pointer_cast<IMDEventWorkspace>(m_rhs);
    }
  }

  this->checkInputs();

  if (m_out == m_lhs) {
    // A = A * B. -> we will do A *= B
  } else {
    // C = A + B. -> So first we clone A (lhs) into C
    IAlgorithm_sptr clone =
        this->createChildAlgorithm("CloneMDWorkspace", 0.0, 0.5, true);
    clone->setProperty("InputWorkspace", m_lhs);
    clone->executeAsChildAlg();
    m_out = clone->getProperty("OutputWorkspace");
  }

  // Okay, at this point we are ready to do, e.g.,
  //  "m_out /= m_rhs"
  if (!m_out)
    throw std::runtime_error("Error creating the output workspace");
  if (!m_rhs)
    throw std::runtime_error("No RHS workspace specified!");

  m_operand_event = boost::dynamic_pointer_cast<IMDEventWorkspace>(m_rhs);
  m_operand_histo = boost::dynamic_pointer_cast<MDHistoWorkspace>(m_rhs);
  m_operand_scalar = boost::dynamic_pointer_cast<WorkspaceSingleValue>(m_rhs);

  m_out_event = boost::dynamic_pointer_cast<IMDEventWorkspace>(m_out);
  m_out_histo = boost::dynamic_pointer_cast<MDHistoWorkspace>(m_out);

  if (m_out_event) {
    // Call the templated virtual function for this type of MDEventWorkspace
    this->execEvent();
  } else if (m_out_histo) {
    // MDHistoWorkspace as the output
    if (m_operand_histo) {
      if (m_out_histo->getNumDims() != m_operand_histo->getNumDims())
        throw std::invalid_argument(
            "Cannot perform " + this->name() +
            " on MDHistoWorkspace's with a different number of dimensions.");
      if (m_out_histo->getNPoints() != m_operand_histo->getNPoints())
        throw std::invalid_argument(
            "Cannot perform " + this->name() +
            " on MDHistoWorkspace's with a different number of points.");

      // Check that the dimensions span the same size, warn if they don't
      for (size_t d = 0; d < m_out_histo->getNumDims(); d++) {
        IMDDimension_const_sptr dimA = m_out_histo->getDimension(0);
        IMDDimension_const_sptr dimB = m_operand_histo->getDimension(0);
        if (dimA->getMinimum() != dimB->getMinimum() ||
            dimA->getMaximum() != dimB->getMaximum())
          g_log.warning() << "Dimension " << d << " (" << dimA->getName()
                          << ") has different extents in the two "
                             "MDHistoWorkspaces. The operation may not make "
                             "sense!" << std::endl;
      }
      this->execHistoHisto(m_out_histo, m_operand_histo);
    } else if (m_operand_scalar)
      this->execHistoScalar(m_out_histo, m_operand_scalar);
    else
      throw std::runtime_error(
          "Unexpected operand workspace type. Expected MDHistoWorkspace or "
          "WorkspaceSingleValue, got " +
          m_rhs->id());

    // When operating on MDHistoWorkspaces, add a simple flag
    // that will be checked in BinMD to avoid binning a modified workspace
    if (m_out_histo->getNumExperimentInfo() == 0) // Create a run if needed
      m_out_histo->addExperimentInfo(ExperimentInfo_sptr(new ExperimentInfo()));
    m_out_histo->getExperimentInfo(0)->mutableRun().addProperty(
        new PropertyWithValue<std::string>("mdhisto_was_modified", "1"), true);
  } else {
    throw std::runtime_error(
        "Unexpected output workspace type. Expected MDEventWorkspace or "
        "MDHistoWorkspace, got " +
        m_out->id());
  }

  // Give the output
  setProperty("OutputWorkspace", m_out);
}