/********************************************
 *  turboPmacController.h
 *
 * Turbo PMAC controller driver based on the asynMotorController class
 *
 * Stefan Mathis, September 2024
 ********************************************/

#ifndef turboPmacController_H
#define turboPmacController_H
#include "sinqAxis.h"
#include "sinqController.h"
#include <memory>

// Forward declaration of the controller class to resolve the cyclic dependency
// between the controller and the axis .h-file. See
// https://en.cppreference.com/w/cpp/language/class.
class HIDDEN turboPmacAxis;

struct HIDDEN turboPmacControllerImpl;

class HIDDEN turboPmacController : public sinqController {
  public:
    /**
     * @brief Construct a new turboPmacController object. This function is meant
    to be called from a child class constructor.
     *
     * @param portName            See sinqController constructor
     * @param ipPortConfigName    See sinqController constructor
     * @param numAxes             See sinqController constructor
     * @param movingPollPeriod    See sinqController constructor
     * @param idlePollPeriod      See sinqController constructor
     * @param comTimeout          When trying to communicate with the device,
    the underlying asynOctetSyncIO interface waits for a response until this
    time (in seconds) has passed, then it declares a timeout.
     * @param numExtraParams      Number of extra parameters from a child class
     */
    turboPmacController(const char *portName, const char *ipPortConfigName,
                        int numAxes, double movingPollPeriod,
                        double idlePollPeriod, double comTimeout,
                        int numExtraParams = 0);

    /**
     * @brief Destroy the controller. Its implementation is empty, however the
     * destructor needs to be provided for handling turboPmacControllerImpl.
     *
     */
    virtual ~turboPmacController();

    /**
     * @brief Get the axis object
     *
     * @param pasynUser           Specify the axis via the asynUser
     * @return turboPmacAxis*        If no axis could be found, this is a
     * nullptr
     */
    turboPmacAxis *getTurboPmacAxis(asynUser *pasynUser);

    /**
     * @brief Get the axis object
     *
     * @param axisNo              Specify the axis via its index
     * @return turboPmacAxis*        If no axis could be found, this is a
     * nullptr
     */
    turboPmacAxis *getTurboPmacAxis(int axisNo);

    /**
     * @brief Overloaded function of sinqController
     *
     * The function is overloaded to allow rereading the encoder and config.
     *
     * @param pasynUser           Specify the axis via the asynUser
     * @param value               New value
     * @return asynStatus
     */
    virtual asynStatus writeInt32(asynUser *pasynUser, epicsInt32 value);

    /**
     * @brief Send a command to the hardware and receive a response
     *
     * @param axisNo              Axis to which the command should be send
     * @param command             Command for the hardware
     * @param response            Buffer for the response. This buffer is
     * expected to have the size MAXBUF_.
     * @param numExpectedResponses  The PMAC MCU can send multiple responses at
     * once. The number of responses is determined by the number of
     * "subcommands" within command. Therefore it is known in advance how many
     * "subresponses" are expected. This can be used to check the integrity of
     * the received response, since the subresponses are separated by carriage
     * returns (/r). The number of carriage returns is compared to
     * numExpectedResponses to determine if the communication was successfull.
     * @return asynStatus
     */
    asynStatus writeRead(int axisNo, const char *command, char *response,
                         int numExpectedResponses)
        __attribute__((visibility("hidden")));

    /**
     * @brief Specialized version of sinqController::couldNotParseResponse
     * for turboPmac
     *
     * This is an overloaded version of
     * sinqController::couldNotParseResponse which calls
     * adjustResponseForLogging on response before handing it over to
     * sinqController::couldNotParseResponse.
     *
     * @param command             Command which led to the unparseable message
     * @param response            Response which wasn't parseable
     * @param axisNo_             Axis where the problem occurred
     * @param functionName        Name of the caller function. It is recommended
    to use a macro, e.g. __func__ or __PRETTY_FUNCTION__.
     * @param lineNumber          Source code line where this function is
    called. It is recommended to use a macro, e.g. __LINE__.
     * @return asynStatus         Returns asynError.
     */
    asynStatus couldNotParseResponse(const char *command, const char *response,
                                     int axisNo_, const char *functionName,
                                     int lineNumber);

    /**
     * @brief Perform a hardware flush (clearing of communication buffers)
     *
     * The PMAC controllers hardware buffers can be flushed (see Turbo PMAC user
     * manual, p. 414). This "freezes" the PMAC for around 10 ms and should
     * therefore only be done if it is necessary (i.e. not as part of the
     * regular communication procedure).
     *
     * @return asynStatus
     */
    asynStatus doFlushHardware();

    // Accessors for additional PVs
    int rereadEncoderPosition();
    int readConfig();
    int flushHardware();
    int limFromHardware();

    asynUser *pasynInt32SyncIOipPort();

  private:
    std::unique_ptr<turboPmacControllerImpl> pTurboPmacC_;

  protected:
};

#endif /* turboPmacController_H */
