/* Copyright 2024 INAF - Osservatorio Astronomico di Cagliari

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
 */

/*! \file tfrfme.h
 *
 * \brief Representation of the trapping calculation objects.
 */

#ifndef INCLUDE_TFRFME_H_
#define INCLUDE_TFRFME_H_

/*! \brief Class to represent the first group of trapping swap data.
 */
class Swap1 {
protected:
  //! Index of the last element to be filled.
  int last_index;
  //! Number of vector coordinates. QUESTION: correct?
  int nkv;
  //! NLMMT = 2 * LM * (LM + 2)
  int nlmmt;

  //! QUESTION: definition?
  dcomplex *wk;

  /*! \brief Load a Swap1 instance from a HDF5 binary file.
   *
   * \param file_name: `string` Name of the file to be loaded.
   * \return instance: `Swap1 *` Pointer to a new Swap1 instance.
   */
  static Swap1 *from_hdf5(const std::string& file_name);

  /*! \brief Load a Swap1 instance from a legacy binary file.
   *
   * \param file_name: `string` Name of the file to be loaded.
   * \return instance: `Swap1 *` Pointer to a new Swap1 instance.
   */
  static Swap1 *from_legacy(const std::string& file_name);

  /*! \brief Save a Swap1 instance to a HDF5 binary file.
   *
   * \param file_name: `string` Name of the file to be written.
   */
  void write_hdf5(const std::string& file_name);

  /*! \brief Save a Swap1 instance to a legacy binary file.
   *
   * \param file_name: `string` Name of the file to be written.
   */
  void write_legacy(const std::string& file_name);

public:
  /*! \brief Swap1 instance constructor.
   *
   * \param lm: `int` Maximum field expansion order.
   * \param _nkv: `int` Number of vector coordinates. QUESTION: correct?
   */
  Swap1(int lm, int _nkv);

  /*! \brief Swap1 instance destroyer.
   */
  ~Swap1() { delete[] wk; }

  /*! \brief Append an element at the end of the vector.
   *
   * \param value: `complex double` The value to be added to the vector.
   */
  void append(dcomplex value) { wk[last_index++] = value; }
  
  /*! \brief Load a Swap1 instance from binary file.
   *
   * \param file_name: `string` Name of the file.
   * \param mode: `string` Format of the file (can be either "HDF5"
   * or "LGEACY". Default is "LEGACY").
   * \return instance: `Swap1 *` Pointer to a newly created Swap1 instance.
   */
  static Swap1* from_binary(const std::string& file_name, const std::string& mode="LEGACY");

  /*! \brief Calculate the necessary amount of memory to create a new instance.
   *
   * \param lm: `int` Maximum field expansion order.
   * \param _nkv: `int` Number of vector coordinates. QUESTION: correct?
   * \return size: `long` The necessary memory size in bytes.
   */
  static long get_memory_requirement(int lm, int _nkv);
  
  /*! \brief Get the pointer to the WK vector.
   *
   * \return value: `complex double *` Memory address of the WK vector.
   */
  dcomplex *get_vector() { return wk; }

  /*! \brief Bring the pointer to the next element at the start of vector.
   */
  void reset() { last_index = 0; }

  /*! \brief Write a Swap1 instance to binary file.
   *
   * \param file_name: `string` Name of the file.
   * \param mode: `string` Format of the file (can be either "HDF5"
   * or "LGEACY". Default is "LEGACY").
   */
  void write_binary(const std::string& file_name, const std::string& mode="LEGACY");

  /*! \brief Test whether two instances of Swap1 are equal.
   *
   * \param other: `Swap1 &` Reference to the instance to be compared
   * with.
   * \return result: `bool` True, if the two instances are equal,
   * false otherwise.
   */
  bool operator ==(Swap1 &other);
};

/*! \brief Class to represent the second group of trapping swap data.
 */
class Swap2 {
protected:
  //! Index of the last vector element to be filled.
  int last_vector;
  //! Index of the last matrix element to be filled.
  int last_matrix;
  //! Number of vector coordinates. QUESTION: correct?
  int nkv;
  //! QUESTION: definition?
  double *vkv;
  //! QUESTION: definition?
  double **vkzm;
  //! QUESTION: definition?
  double apfafa;
  //! QUESTION: definition?
  double pmf;
  //! QUESTION: definition?
  double spd;
  //! QUESTION: definition?
  double rir;
  //! QUESTION: definition?
  double ftcn;
  //! QUESTION: definition?
  double fshmx;
  //! QUESTION: definition?
  double vxyzmx;
  //! Cartesian displacement. QUESTION: correct?
  double delxyz;
  //! QUESTION: definition?
  double vknmx;
  //! QUESTION: definition?
  double delk;
  //! QUESTION: definition?
  double delks;
  //! NLMMT = LM * (LM + 2) * 2
  int nlmmt;
  //! Number of radial vector coordinates. QUESTION: correct?
  int nrvc;

  /*! \brief Load a Swap2 instance from a HDF5 binary file.
   *
   * \param file_name: `string` Name of the file to be loaded.
   * \return instance: `Swap2 *` Pointer to a new Swap2 instance.
   */
  static Swap2 *from_hdf5(const std::string& file_name);

  /*! \brief Load a Swap2 instance from a legacy binary file.
   *
   * \param file_name: `string` Name of the file to be loaded.
   * \return instance: `Swap2 *` Pointer to a new Swap2 instance.
   */
  static Swap2 *from_legacy(const std::string& file_name);

  /*! \brief Save a Swap2 instance to a HDF5 binary file.
   *
   * \param file_name: `string` Name of the file to be written.
   */
  void write_hdf5(const std::string& file_name);

  /*! \brief Save a Swap2 instance to a legacy binary file.
   *
   * \param file_name: `string` Name of the file to be written.
   */
  void write_legacy(const std::string& file_name);

public:
  /*! \brief Swap2 instance constructor.
   *
   * \param _nkv: `int` Number of vector coordinates. QUESTION: correct?
   */
  Swap2(int _nkv);

  /*! \brief Swap2 instance destroyer.
   */
  ~Swap2();
  
  /*! \brief Load a Swap2 instance from binary file.
   *
   * \param file_name: `string` Name of the file.
   * \param mode: `string` Format of the file (can be either "HDF5"
   * or "LGEACY". Default is "LEGACY").
   * \return instance: `Swap2 *` Pointer to a newly created Swap2 instance.
   */
  static Swap2* from_binary(const std::string& file_name, const std::string& mode="LEGACY");

  /*! \brief Get the pointer to the VKZM matrix.
   *
   * \return value: `double **` Pointer to the VKZM matrix.
   */
  double **get_matrix() { return vkzm; }

  /*! \brief Calculate the necessary amount of memory to create a new instance.
   *
   * \param _nkv: `int` Number of radial vector coordinates. QUESTION: correct?
   * \return size: `long` The necessary memory size in bytes.
   */
  static long get_memory_requirement(int _nkv);

  /*! \brief Get a parameter by its name.
   *
   * \param param_name: `string` Name of the parameter.
   * \return value: `double` The value of the requested parameter.
   */
  double get_param(const std::string& param_name);

  /*! \brief Get the pointer to the VKV vector.
   *
   * \return value: `double *` Pointer to the VKV vector.
   */
  double *get_vector() { return vkv; }

  /*! \brief Append an element at the end of the matrix.
   *
   * \param value: `double` The value to be pushed in the matrix.
   */
  void push_matrix(double value);

  /*! \brief Append an element at the end of the vector.
   *
   * \param value: `double` The value to be pushed in the vector.
   */
  void push_vector(double value) { vkv[last_vector++] = value; }

  /*! \brief Bring the matrix pointer to the start of the array.
   */
  void reset_matrix() { last_matrix = 0; }

  /*! \brief Bring the vector pointer to the start of the array.
   */
  void reset_vector() { last_vector = 0; }

  /*! \brief Set a parameter by its name and value.
   *
   * \param param_name: `string` Name of the parameter.
   * \param value: `double` The value of the parameter.
   */
  void set_param(const std::string& param_name, double value);

  /*! \brief Write a Swap2 instance to binary file.
   *
   * \param file_name: `string` Name of the file.
   * \param mode: `string` Format of the file (can be either "HDF5"
   * or "LGEACY". Default is "LEGACY").
   */
  void write_binary(const std::string& file_name, const std::string& mode="LEGACY");

  /*! \brief Test whether two instances of Swap2 are equal.
   *
   * \param other: `Swap1 &` Reference to the instance to be compared
   * with.
   * \return result: `bool` True, if the two instances are equal,
   * false otherwise.
   */
  bool operator ==(Swap2 &other);
};

/*! \brief Class to represent the trapping configuration.
 */
class TFRFME {
protected:
  //! NLMMT = 2 * LM * (LM + 2)
  int nlmmt;
  //! NRVC = NXV * NYV * NZV
  int nrvc;
  
  //! Field expansion mode identifier.
  int lmode;
  //! Maximim field expansion order;
  int lm;
  //! QUESTION: definition?
  int nkv;
  //! Number of computed X coordinates.
  int nxv;
  //! Number of computed Y coordinates.
  int nyv;
  //! Number of computed Z coordinates.
  int nzv;
  //! Wave number in scale units
  double vk;
  //! External medium refractive index
  double exri;
  //! QUESTION: definition?
  double an;
  //! QUESTION: definition?
  double ff;
  //! QUESTION: definition?
  double tra;
  //! QUESTION: definition?
  double spd;
  //! QUESTION: definition?
  double frsh;
  //! QUESTION: definition?
  double exril;
  //! Vector of computed x positions
  double *xv;
  //! Vector of computed y positions
  double *yv;
  //! Vector of computed z positions
  double *zv;
  //! QUESTION: definition?
  dcomplex **wsum;

  /*! \brief Load a configuration instance from a HDF5 binary file.
   *
   * \param file_name: `string` Name of the file to be loaded.
   * \return instance: `TFRFME *` Pointer to a new trapping configuration
   * instance.
   */
  static TFRFME *from_hdf5(const std::string& file_name);

  /*! \brief Load a configuration instance from a legacy binary file.
   *
   * \param file_name: `string` Name of the file to be loaded.
   * \return instance: `TFRFME *` Pointer to a new trapping configuration
   * instance.
   */
  static TFRFME *from_legacy(const std::string& file_name);

  /*! \brief Save a configuration instance to a HDF5 binary file.
   *
   * \param file_name: `string` Name of the file to be written.
   */
  void write_hdf5(const std::string& file_name);

  /*! \brief Save a configuration instance to a legacy binary file.
   *
   * \param file_name: `string` Name of the file to be written.
   */
  void write_legacy(const std::string& file_name);

public:
  /*! \brief Trapping configuration instance constructor.
   *
   * \param _lmode: `int` Order expansion mode flag.
   * \param _lm: `int` Maximum field expansion order.
   * \param _nkv: `int` Number of wave vector coordinates. QUESTION: correct?
   * \param _nxv: `int` Number of computed X coordinates.
   * \param _nyv: `int` Number of computed Y coordinates.
   * \param _nzv: `int` Number of computed Z coordinates.
   */
  TFRFME(
	 int _lmode, int _lm, int _nkv, int _nxv, int _nyv, int _nzv
);
  
  /*! \brief Trapping configuration instance destroyer.
   */
  ~TFRFME();

  /*! \brief Load a trapping configuration instance from binary file.
   *
   * \param file_name: `string` Name of the file.
   * \param mode: `string` Format of the file (can be either "HDF5"
   * or "LGEACY". Default is "LEGACY").
   * \return instance: `TFRFME *` Pointer to a newly created configuration
   * instance.
   */
  static TFRFME* from_binary(const std::string& file_name, const std::string& mode="LEGACY");

  /*! \brief Get the pointer to the WSUM matrix.
   *
   * \return value: `complex double **` Pointer to the WSUM matrix.
   */
  dcomplex **get_matrix() { return wsum; }

  /*! \brief Calculate the necessary amount of memory to create a new instance.
   *
   * \param _lmode: `int` Order expansion mode flag.
   * \param _lm: `int` Maximum field expansion order.
   * \param _nkv: `int` Number of radial vector coordinates. QUESTION: correct?
   * \param _nxv: `int` Number of computed X coordinates.
   * \param _nyv: `int` Number of computed Y coordinates.
   * \param _nzv: `int` Number of computed Z coordinates.
   * \return size: `long` The necessary memory size in bytes.
   */
  static long get_memory_requirement(
				     int _lmode, int _lm, int _nkv, int _nxv,
				     int _nyv, int _nzv
  );

  /*! \brief Get a configuration parameter.
   *
   * \param param_name: `string` Name of the parameter.
   * \return value: `double` The value of the requested parameter.
   */
  double get_param(const std::string& param_name);

  /*! \brief Get the pointer to the X coordinates vector.
   *
   * \return x: `double *` Pointer to X coordinates vector.
   */
  double *get_x() { return xv; }

  /*! \brief Get the pointer to the Y coordinates vector.
   *
   * \return y: `double *` Pointer to Y coordinates vector.
   */
  double *get_y() { return yv; }

  /*! \brief Get the pointer to the Z coordinates vector.
   *
   * \return z: `double *` Pointer to Z coordinates vector.
   */
  double *get_z() { return zv; }

  /*! \brief Set a configuration parameter.
   *
   * \param param_name: `string` Name of the parameter.
   * \param value: `double` Value to be stored as parameter.
   */
  void set_param(const std::string& param_name, double value);

  /*! \brief Write a trapping configuration instance to binary file.
   *
   * \param file_name: `string` Name of the file.
   * \param mode: `string` Format of the file (can be either "HDF5"
   * or "LGEACY". Default is "LEGACY").
   */
  void write_binary(const std::string& file_name, const std::string& mode="LEGACY");
  
  /*! \brief Test whether two instances of configuration are equal.
   *
   * \param other: `TFRFME &` Reference to the instance to be compared
   * with.
   * \return result: `bool` True, if the two instances are equal,
   * false otherwise.
   */
  bool operator ==(const TFRFME& other);
};
#endif
