//  ************************************************************************************************
//
//  BornAgain: simulate and fit reflection and scattering
//
//! @file      Device/Coord/CoordSystem1D.h
//! @brief     Defines CoordSystem1D class and derived classes.
//!
//! @homepage  http://www.bornagainproject.org
//! @license   GNU General Public License v3 or higher (see COPYING)
//! @copyright Forschungszentrum Jülich GmbH 2018
//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
//
//  ************************************************************************************************

#ifdef SWIG
#error no need to expose this header to Swig
#endif // SWIG
#ifndef BORNAGAIN_DEVICE_COORD_COORDSYSTEM1D_H
#define BORNAGAIN_DEVICE_COORD_COORDSYSTEM1D_H

#include "Device/Coord/ICoordSystem.h"
#include <functional>

//! Abstract base class to support coordinate transforms and axis labels for 1D scans.
//! Inherited by AngularReflectometryCoords and WavenumberReflectometryCoords.

class CoordSystem1D : public ICoordSystem {
public:
    CoordSystem1D(const Scale* axis); //!< Takes ownership of axis
    ~CoordSystem1D() override = default;

    CoordSystem1D* clone() const override = 0;

    //! Creates axis in converted units.
    Scale* convertedAxis(size_t i_axis, Coords units) const override;

    //! Calculates minimum on-axis value in given units.
    double calculateMin(size_t i_axis, Coords units) const override;

    //! Calculates maximum on-axis value in given units.
    double calculateMax(size_t i_axis, Coords units) const override;

private:
    //! Returns translating functional (rads --> output units)
    virtual std::function<double(double)> getTraslatorTo(Coords units) const = 0;
};


//! Conversion of axis units for the case of conventional (angle-based) reflectometry.
class AngularReflectometryCoords : public CoordSystem1D {
public:
    //! Constructs the object for unit conversion.
    AngularReflectometryCoords(double wavelength, const Scale& axis,
                               Coords axis_units = Coords::RADIANS);
    ~AngularReflectometryCoords() override;

    AngularReflectometryCoords* clone() const override;

    static std::vector<Coords> availableUnits0();
    static std::string nameOfAxis0(Coords units);

private:
    //! Returns the list of all available units
    std::vector<Coords> availableUnits() const override;

    std::string nameOfAxis(size_t i_axis, Coords units) const override;

    //! Returns default units to convert to.
    Coords defaultUnits() const override { return Coords::DEGREES; }

    AngularReflectometryCoords(const AngularReflectometryCoords& other);

    //! Returns translating functional (rads --> desired units)
    std::function<double(double)> getTraslatorTo(Coords units) const override;

    double m_wavelength; //!< basic wavelength in nm (for translation to q-space).
};


//! Conversion of axis units for the case of q-defined reflectometry.
class WavenumberReflectometryCoords : public CoordSystem1D {
public:
    WavenumberReflectometryCoords(const Scale* axis);
    ~WavenumberReflectometryCoords() override;

    WavenumberReflectometryCoords* clone() const override;

private:
    //! Returns the list of all available units
    std::vector<Coords> availableUnits() const override;

    std::string nameOfAxis(size_t i_axis, Coords units) const override;

    //! Returns default units to convert to.
    Coords defaultUnits() const override { return Coords::QSPACE; }

    WavenumberReflectometryCoords(const WavenumberReflectometryCoords& other);

    //! Returns translating functional (inv. nm --> desired units)
    std::function<double(double)> getTraslatorTo(Coords units) const override;
};

#endif // BORNAGAIN_DEVICE_COORD_COORDSYSTEM1D_H
