//  ************************************************************************************************
//
//  BornAgain: simulate and fit reflection and scattering
//
//! @file      Device/Detector/RectangularDetector.h
//! @brief     Defines class RectangularDetector.
//!
//! @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)
//
//  ************************************************************************************************

#ifndef BORNAGAIN_DEVICE_DETECTOR_RECTANGULARDETECTOR_H
#define BORNAGAIN_DEVICE_DETECTOR_RECTANGULARDETECTOR_H

#include "Device/Detector/IDetector.h"

class IPixel;
class RectangularPixel;

//! A flat rectangular detector with axes and resolution function.

class RectangularDetector : public IDetector {
public:
    enum EDetectorArrangement {
        GENERIC,
        PERPENDICULAR_TO_SAMPLE,
        PERPENDICULAR_TO_DIRECT_BEAM,
        PERPENDICULAR_TO_REFLECTED_BEAM
    };

    RectangularDetector(std::array<std::shared_ptr<Scale>, 2> axes);

    //! Rectangular detector constructor
    //! @param nxbins Number of bins (pixels) in x-direction
    //! @param width Width of the detector in mm along x-direction
    //! @param nybins Number of bins (pixels) in y-direction
    //! @param height Height of the detector in mm along y-direction
    RectangularDetector(size_t nxbins, double width, size_t nybins, double height);

    RectangularDetector(const RectangularDetector& other);
    ~RectangularDetector() override;

    RectangularDetector* clone() const override;
    std::string className() const final { return "RectangularDetector"; }

    void setDetectorNormal(const R3& k) override;

    void setDetectorPosition(R3 normal_to_detector, double u0, double v0,
                             R3 direction = R3(0.0, -1.0, 0.0));

    void setPerpendicularToSampleX(double distance, double u0, double v0);
    void setPerpendicularToDirectBeam(double distance, double u0, double v0);
    void setPerpendicularToReflectedBeam(double distance, double u0 = 0.0, double v0 = 0.0);

    double width() const;
    double height() const;
    size_t xSize() const;
    size_t ySize() const;
    R3 getNormalVector() const;
    double getU0() const;
    double getV0() const;
    R3 getDirectionVector() const;
    double getDistance() const;
    double getDirectBeamU0() const;
    double getDirectBeamV0() const;
    EDetectorArrangement getDetectorArrangment() const;

    //! Returns default axes units
    Coords defaultCoords() const override { return Coords::MM; }

    const RectangularPixel* regionOfInterestPixel() const;
    const CoordSystem2D* scatteringCoords(const Beam& beam) const override;

private:
    //! Creates an IPixel for the given Datafield object and index
    const IPixel* createPixel(size_t index) const override;

    //! Returns index of pixel that contains the specular wavevector.
    //! If no pixel contains this specular wavevector, the number of pixels is
    //! returned. This corresponds to an overflow index.
    size_t indexOfSpecular(const Beam& beam) const override;

    //! swap function
    void swapContent(RectangularDetector& other);

    void setDistanceAndOffset(double distance, double u0, double v0);
    void initNormalVector(R3 central_k);
    void initUandV();

    R3 m_normal_to_detector;
    double m_u0, m_v0; //!< position of normal vector hitting point in detector coordinates
    R3 m_direction;    //!< direction vector of detector coordinate system
    double m_distance; //!< distance from sample origin to the detector plane
    double m_dbeam_u0, m_dbeam_v0; //!< position of direct beam in detector coordinates
    EDetectorArrangement m_detector_arrangement;
    R3 m_u_unit;
    R3 m_v_unit;
};

#endif // BORNAGAIN_DEVICE_DETECTOR_RECTANGULARDETECTOR_H
