#ifndef turboPmacAXIS_H
#define turboPmacAXIS_H
#include "sinqController.h"
#include "turboPmacController.h"
#include <memory>

struct HIDDEN turboPmacAxisImpl;

class HIDDEN turboPmacAxis : public sinqAxis {
  public:
    /**
     * @brief Construct a new turboPmacAxis
     *
     * @param pController         Pointer to the associated controller
     * @param axisNo              Index of the axis
     * @param initialize          By setting this parameter to false, the
     * initialization functions of the axes are not executed. This is e.g.
     * necessary when this constructor is called from a children class
     * constructor which performs its own initialization.
     */
    turboPmacAxis(turboPmacController *pController, int axisNo,
                  bool initialize = true);

    /**
     * @brief Destroy the turboPmacAxis
     *
     * This destructor is necessary in order to use the PIMPL idiom.
     */
    virtual ~turboPmacAxis();

    /**
     * @brief Readout of some values from the controller at IOC startup
     *
     * The following steps are performed:
     * - Read out the motor status, motor position, velocity and acceleration
     * from the MCU and store this information in the parameter library.
     * - Set the enable PV according to the initial status of the axis.
     *
     * @return asynStatus
     */
    virtual asynStatus init();

    /**
     * @brief Implementation of the `stop` function from asynMotorAxis
     *
     * @param acceleration        Acceleration ACCEL from the motor record. This
     * value is currently not used.
     * @return asynStatus
     */
    virtual asynStatus stop(double acceleration);

    /**
     * @brief Implementation of the `doHome` function from sinqAxis. The
     * parameters are described in the documentation of `sinqAxis::doHome`.
     *
     * @param minVelocity
     * @param maxVelocity
     * @param acceleration
     * @param forwards
     * @return asynStatus
     */
    virtual asynStatus doHome(double minVelocity, double maxVelocity,
                              double acceleration, int forwards);

    /**
     * @brief Implementation of the `doPoll` function from sinqAxis. The
     * parameters are described in the documentation of `sinqAxis::doPoll`.
     *
     * @param moving
     * @return asynStatus
     */
    virtual asynStatus doPoll(bool *moving);

    /**
     * @brief Implementation of the `doMove` function from sinqAxis. The
     * parameters are described in the documentation of `sinqAxis::doMove`.
     *
     * @param position
     * @param relative
     * @param min_velocity
     * @param max_velocity
     * @param acceleration
     * @return asynStatus
     */
    virtual asynStatus doMove(double position, int relative,
                              double min_velocity, double max_velocity,
                              double acceleration);

    /**
     * @brief Implementation of the `doReset` function from sinqAxis.
     *
     * @param on
     * @return asynStatus
     */
    virtual asynStatus doReset();

    /**
     * @brief Enable / disable the axis.
     *
     * @param on
     * @return asynStatus
     */
    virtual asynStatus enable(bool on);

    /**
     * @brief Read the encoder type (incremental or absolute) for this axis from
     * the MCU and store the information in the PV ENCODER_TYPE.
     *
     * @return asynStatus
     */
    virtual asynStatus readEncoderType();

    /**
     * @brief Trigger a rereading of the encoder position.
     *
     * @return asynStatus
     */
    virtual asynStatus rereadEncoder();

    /**
     * @brief Interpret the error code and populate the user message accordingly
     *
     * @param error
     * @param userMessage
     * @param sizeUserMessage
     * @return asynStatus
     */
    asynStatus handleError(int error, char *userMessage, int sizeUserMessage);

    /**
     * @brief Check if the axis needs to run its initialization function
     *
     * @return true
     * @return false
     */
    bool needInit();

    /**
     * @brief Instruct the axis to run its init() function during the next poll
     *
     * @param needInit
     */
    void setNeedInit(bool needInit);

    /**
     * @brief Return a pointer to the axis controller
     */
    virtual turboPmacController *pController() override { return pC_; };

  private:
    turboPmacController *pC_;
    std::unique_ptr<turboPmacAxisImpl> pTurboPmacA_;
};

#endif
