// SPDX-FileCopyrightText: Copyright (c) Kitware Inc.
// SPDX-License-Identifier: BSD-3-Clause
/**
 * @class   vtkSMCoreUtilities
 * @brief   collection of utilities.
 *
 * vtkSMCoreUtilities provides miscellaneous utility functions.
 */

#ifndef vtkSMCoreUtilities_h
#define vtkSMCoreUtilities_h

#include "vtkObject.h"
#include "vtkRemotingServerManagerModule.h" //needed for exports

#include <optional> // for std::optional
#include <string>   // for std::string
#include <vector>   // for std::vector

class vtkSMProxy;
class vtkSMStringVectorProperty;

class VTKREMOTINGSERVERMANAGER_EXPORT vtkSMCoreUtilities : public vtkObject
{
public:
  static vtkSMCoreUtilities* New();
  vtkTypeMacro(vtkSMCoreUtilities, vtkObject);
  void PrintSelf(ostream& os, vtkIndent indent) override;

  /**
   * Given a proxy (or proxy prototype), returns the name of the property that
   * ParaView application will be use as the default FileName property.
   * Returns the name of the property or nullptr when no such property is found.
   */
  static const char* GetFileNameProperty(vtkSMProxy*);

  /**
   * Returns list of all properties that accept filenames in order that they are
   * defined.
   */
  static std::vector<std::string> GetFileNameProperties(vtkSMProxy* proxy);

  /**
   * Sanitize a label/name to be remove spaces, delimiters etc.
   */
  static std::string SanitizeName(const char*);
  static std::string SanitizeName(const std::string& name)
  {
    return vtkSMCoreUtilities::SanitizeName(name.c_str());
  }

  ///@{
  /**
   * Given a range, converts it to be a valid range to switch to log space. If
   * the range is changed, returns true, otherwise returns false.
   */
  static bool AdjustRangeForLog(double range[2]);
  static bool AdjustRangeForLog(double& rmin, double& rmax)
  {
    double range[2] = { rmin, rmax };
    bool retVal = vtkSMCoreUtilities::AdjustRangeForLog(range);
    rmin = range[0];
    rmax = range[1];
    return retVal;
  }
  ///@}

  ///@{
  /**
   * Adjust the given range to make it suitable for use with color maps. The
   * current logic (which may change in future) does the following:
   * 1. If the range is invalid i.e range[1] < range[0], simply returns `false`
   *    and keeps the range unchanged.
   * 2. If the range[0] == range[1] (using logic to handle nearly similar
   *    floating points numbers), then the range[1] is adjusted to be such that
   *    range[1] > range[0].
   * 3. If range[0] < range[1] (beyond the margin of error checked for in (2),
   *    then range is left unchanged.
   *
   * @returns `true` if the range was changed, `false` is the range was left
   * unchanged.
   */
  static bool AdjustRange(double range[2]);
  static bool AdjustRange(double& rmin, double& rmax)
  {
    double range[2] = { rmin, rmax };
    bool retVal = vtkSMCoreUtilities::AdjustRange(range);
    rmin = range[0];
    rmax = range[1];
    return retVal;
  }
  ///@}

  ///@{
  /**
   * Compares \c val1 and \c val2 and returns true is the two numbers are within
   * \c ulpsDiff ULPs (units in last place) from each other.
   */
  static bool AlmostEqual(const double range[2], int ulpsDiff);
  static bool AlmostEqual(double rmin, double rmax, int ulpsDiff)
  {
    double range[2] = { rmin, rmax };
    return vtkSMCoreUtilities::AlmostEqual(range, ulpsDiff);
  }
  ///@}

  ///@{
  /**
   * Given a proxy and a port number get the name of the input.
   */
  static const char* GetInputPropertyName(vtkSMProxy* proxy, int port = 0);
  ///@}

  /**
   * Given a VTK cell type value from the enum in vtkCellTypes.h,
   * returns a string describing that cell type for use if ParaView's GUI.
   * For example it pasesd VTK_TRIANGLE it will return "Triangle".
   * If an unknown cell type is passed to this it returns the string "Unknown".
   */
  static const char* GetStringForCellType(int cellType);

  ///@{
  /**
   * Replaces input proxy, which should be a reader proxy, by a new proxy in the pipeline.
   * The new input files are stored in files, and `propName` should be the file name property
   * in the reader (usually "FileName" or "FileNames").
   * Optionally, the `userModifiedName` flag can be specified to tell if the proxy name has been
   * modified by the user. In case the user modified the name, we keep it in the replacement
   * process. `userModifiedName`  is set to false when calling the function without the flag
   * argument.
   */
  static void ReplaceReaderFileName(
    vtkSMProxy* proxy, const std::vector<std::string>& files, const char* propName)
  {
    vtkSMCoreUtilities::ReplaceReaderFileName(proxy, files, propName, false);
  }
  static void ReplaceReaderFileName(vtkSMProxy* proxy, const std::vector<std::string>& files,
    const char* propName, bool userModifiedName);
  ///@}

  /**
   * Find the largest prefix that matches all file names in files, and then append a '*'
   * to signify that it is a collection of files. If they all start with something different,
   * this returns '*'.
   */
  static std::string FindLargestPrefix(const std::vector<std::string>& files);

  /**
   * Return the registration name assigned to the given proxy by looking for a property called
   * "RegistrationName". If it does not exist, it returns std::nullopt.
   */
  static std::optional<std::string> RecoverRegistrationName(vtkSMProxy* proxy);

  //@{
  /**
   * Return the name that the proxy will take depending on a list of files given by te `fileNames`
   * parameter or `stringVectorProperty`.
   */
  static std::string GetFileNameFromFileNameList(const std::vector<std::string>& fileNames);
  static std::string GetFileNameFromFileNameList(vtkSMStringVectorProperty* stringVectorProperty);
  //@}

protected:
  vtkSMCoreUtilities();
  ~vtkSMCoreUtilities() override;

private:
  vtkSMCoreUtilities(const vtkSMCoreUtilities&) = delete;
  void operator=(const vtkSMCoreUtilities&) = delete;
};

#endif
