// Mantid Repository : https://github.com/mantidproject/mantid
//
// Copyright &copy; 2019 ISIS Rutherford Appleton Laboratory UKRI,
//   NScD Oak Ridge National Laboratory, European Spallation Source,
//   Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
// SPDX - License - Identifier: GPL - 3.0 +
#pragma once

//----------------------------------------------------------------------
// Includes
//----------------------------------------------------------------------
#include "MantidAPI/IDomainCreator.h"
#include "MantidCurveFitting/DllConfig.h"
#include "MantidDataObjects/TableWorkspace.h"
#include "MantidKernel/cow_ptr.h"

#include <list>
#include <memory>
#include <utility>

namespace Mantid {
namespace API {
class FunctionDomain;
class FunctionDomain1D;
class FunctionValues;
class TableWorkspace;
} // namespace API

namespace CurveFitting {

class MANTID_CURVEFITTING_DLL TableWorkspaceDomainCreator
    : public API::IDomainCreator {
public:
  // Constructor
  TableWorkspaceDomainCreator(Kernel::IPropertyManager *fit,
                              const std::string &workspacePropertyName,
                              DomainType domainType = Simple);
  /// Constructor
  TableWorkspaceDomainCreator(DomainType domainType = Simple);

  // Declare properties that specify the dataset within the workspace to fit to.
  void declareDatasetProperties(const std::string &suffix = "",
                                bool addProp = true) override;

  // Create a domain from the input workspace
  void createDomain(std::shared_ptr<API::FunctionDomain> &domain,
                    std::shared_ptr<API::FunctionValues> &values,
                    size_t i0 = 0) override;

  // Create an output workspace.
  std::shared_ptr<API::Workspace> createOutputWorkspace(
      const std::string &baseName, API::IFunction_sptr function,
      std::shared_ptr<API::FunctionDomain> domain,
      std::shared_ptr<API::FunctionValues> values,
      const std::string &outputWorkspacePropertyName) override;

  /// Set the workspace
  /// @param ws :: workspace to set.
  void setWorkspace(API::ITableWorkspace_sptr ws) {
    m_tableWorkspace = std::move(ws);
  }
  /// Set the startX and endX
  /// @param startX :: Start of the domain
  /// @param endX :: End of the domain
  void setRange(double startX, double endX) {
    m_startX = startX;
    m_endX = endX;
  }
  /// Set max size for Sequential and Parallel domains
  /// @param maxSize :: Maximum size of each simple domain
  void setMaxSize(size_t maxSize) { m_maxSize = maxSize; }

  /// Set the names Of the x, y and error columns
  /// @param xColName :: name of the x column
  /// @param yColName :: name of the y column
  /// @param errColName :: name of the y error column
  void setColumnNames(const std::string &xColName, const std::string &yColName,
                      const std::string &errColName) const {
    m_xColName = xColName;
    m_yColName = yColName;
    m_errColName = errColName;
  }

  size_t getDomainSize() const override;

  void initFunction(API::IFunction_sptr function) override;

private:
  /// Calculate size and starting iterator in the X array
  std::pair<size_t, size_t> getXInterval(std::vector<double> XData) const;
  /// Set all parameters
  void setParameters() const;
  /// Set the names of the X, Y and Error columns
  void setXYEColumnNames(const API::ITableWorkspace_sptr &ws) const;
  /// Creates the blank output workspace of the correct size
  std::shared_ptr<API::MatrixWorkspace>
  createEmptyResultWS(const size_t nhistograms, const size_t nyvalues);
  /// Set initial values for parameters with default values.
  void setInitialValues(API::IFunction &function);
  // Unrolls function into its constituent parts if it is a composite and adds
  // it to the list. Note this is recursive
  void
  appendCompositeFunctionMembers(std::list<API::IFunction_sptr> &functionList,
                                 const API::IFunction_sptr &function) const;
  // Create separate Convolutions for each component of the model of a
  // convolution
  void appendConvolvedCompositeFunctionMembers(
      std::list<API::IFunction_sptr> &functionList,
      const API::IFunction_sptr &function) const;
  /// Add the calculated function values to the workspace
  void addFunctionValuesToWS(
      const API::IFunction_sptr &function,
      std::shared_ptr<API::MatrixWorkspace> &ws, const size_t wsIndex,
      const std::shared_ptr<API::FunctionDomain> &domain,
      const std::shared_ptr<API::FunctionValues> &resultValues) const;
  /// Check workspace is in the correct form
  void setAndValidateWorkspace(const API::Workspace_sptr &ws) const;

  /// Store workspace property name
  std::string m_workspacePropertyName;
  /// Store startX property name
  std::string m_startXPropertyName;
  /// Store endX property name
  std::string m_endXPropertyName;
  /// Store XColumnName property name
  std::string m_xColumnPropertyName;
  /// Store YColumnName property name
  std::string m_yColumnPropertyName;
  /// Store errorColumnName property name
  std::string m_errorColumnPropertyName;

  /// The input TableWorkspace
  mutable API::ITableWorkspace_sptr m_tableWorkspace;
  /// startX
  mutable double m_startX;
  /// endX
  mutable double m_endX;
  /// Max size for seq domain
  mutable size_t m_maxSize;
  /// Ranges that must be excluded from fit
  mutable std::vector<double> m_exclude;
  /// Store the created domain and values
  std::weak_ptr<API::FunctionDomain1D> m_domain;
  std::weak_ptr<API::FunctionValues> m_values;
  /// Store maxSize property name
  std::string m_maxSizePropertyName;
  /// Store the Exclude property name
  std::string m_excludePropertyName;

  /// Store number of the first row used in fitting
  size_t m_startRowNo;
  /// Store the X column name
  mutable std::string m_xColName;
  /// Store the Y column name
  mutable std::string m_yColName;
  /// Store the Y Error column name
  mutable std::string m_errColName;

  /// Flag to indicate if no error column was found
  mutable bool m_noErrCol;
};

} // namespace CurveFitting
} // namespace Mantid
