/*! @file
 *  @brief This file keeps the generic interface of the Motor controller N-Device.
 *
 * A N-Device is a device that can control several motor devices at the same time.
 *
 * @author Frank Kittmann
 $Header: https://svn.mpia.de/gulli/TwiceAsNice/branch/unstable/TwiceAsNice/basda-mocca/src/interface/Basda/Mocca/NDevice.h 16130 2019-11-26 14:31:10Z mathar $
 * @version 10.03.12
 * @date 2010.03.12
 */

#pragma once

#include <Nice/Property/BitSet.h>
#include <Nice/Map/NPoint.h>
#include <Basda/Mocca/Device.h>

namespace Basda
{
    /**
     * @brief This is a specialization of the motor device class.
     *
     * All MoCon devices need to inherit from that class and have to implement
     * at least these methods.
     *
     * @author Frank Kittmann
     * @version 09.06.23
     * @date 2009.06.23
     */
    class MoccaNDevice : public Basda::MoccaDevice
    {
        public:
            /**
             * @brief Constructor
             *
             * @param _config the root of the configuration tree
             */
            MoccaNDevice(const Nice::JointPtr &_config)
                    : MoccaDevice(_config)
            {
            }
            ;

            /**
             * @brief Constructor
             */
            virtual ~MoccaNDevice()
            {
            }
            ;

            /**
             * @brief This method send the command string to the electronic.
             *
             * This method is set to public due to requests from Lorenzo Busoni.
             * BUT THE USER OF THIS METHOD TAKE OVER THE RESPONSIBILITY OF THE
             * CORRECT ERROR HANDLING.
             * THE DRIVER DOES NOT HAVE THE CONTROL OVER THAT DEVICE ANY MORE.
             *
             * @param _commandID  the command ID
             * @param _selectID   the sub command ID
             * @param _cardNo     the ID of the MPIA electronic card
             * @param _moduleNo   the ID of the device module
             * @param _parameter  the parameter as string
             * @param _numberOfLines the number of lines that might be read
             *
             * @see dontParse
             */
            virtual void chat(std::string _cardNo,
                              std::string _commandID,
                              std::string _moduleNo,
                              std::string _selectID,
                              std::string _parameter,
                              std::string _numberOfLines,
                              Basda::DeviceAsync &_async =
                                      Basda::Device::s_async) = 0;

            /**
             * @brief This method was requested by Lorenzo Busoni (lbusoni@arcetri.astro.it 02.03.2012)
             *
             * It supports a direct communication with the MoCon.
             * BUT THE USER OF THIS METHOD TAKE OVER THE RESPONSIBILITY OF THE
             * CORRECT ERROR HANDLING.
             * THE DRIVER DOES NOT HAVE THE CONTROL OVER THAT DEVICE ANY MORE.
             *
             * Hi Martin,
             *
             * that's exactly what I was thinking:
             * The use case is (real example from our experience):
             * we discover at the telescope that we need to continuously monitor a
             * given property (PropA) of the MoCon (obviously because of a
             * malfunctioning in the hardware!). Without this monitor, we will be
             * stuck. You can add the required functionality in the Mocca adding the
             * function getPropA: this requires 4 hours and the day is lost. The day
             * after you are stuck because you discover that you miss PropB, and the
             * day is lost again.
             * As an alternative, one can add the 2 lines below in the code (with a
             * comment: "1 10 0 = getPropA") and this requires 5 minutes. The day
             * after one can add  a similar line with 1 11 0. When you're back in
             * Europe you implement getPropA and getPropB in Mocca and you cleanup
             * the code removing the receiveResponseForComment
             *
             * Generally speaking I think it is a safe idea to have a generic
             * interface to access _EVERY_ possible functionality of a device,
             * because soon or later you'll need it. In the case of the Mocon it is
             * quite easy, a good compromise between living dangerously and wasting
             * too much time in the effort of a complete implementation of all the
             * MoCon commands.
             *
             * Lorenzo Busoni
             *
             * @param _ret the allocated memory for the command response
             *
             */
            virtual std::string dontParse(Basda::DeviceAsync &_async =
                    Basda::Device::s_async) = 0;

            virtual std::string receiveDataRaw(std::string _cardNo,
                                               std::string _commandID,
                                               std::string _moduleNo,
                                               std::string _numberOfLines,
                                               DeviceAsync & = s_async) = 0;

            virtual bool isReachable() = 0;

            virtual void prepareMoveSync(Nice::BitSet _motors) = 0;
            virtual void startMoveSync() = 0;
            /**
             * @brief This method setup the acceleration  anv velocity for a
             *        direct point to point move.
             *
             * @param _acceleration
             * @param _velocity
             * @param _deceleration
             */
            virtual void prepareDirectMove(const Nice::NPoint &_acceleration,
                                   const Nice::NPoint &_velocity,
                                   const Nice::NPoint &_deceleration) = 0;

            /**
             * @brief All motors of a device move to the absolute position.
             *
             * @param _positions a vector containing the absolute target position
             *                   in steps. The order of the points is corresponding
             *                   to the order of the motor names in the property
             *                   tree (alphabetical).
             * @param _async the call back object
             */
            virtual void moveAbsolute(const Nice::NPoint &_positions,
                                      Basda::DeviceAsync &_async =
                                              Basda::Device::s_async) = 0;
            virtual void moveAbsoluteStart(const Nice::NPoint &_positions,
                                      Basda::DeviceAsync & =
                                              Basda::Device::s_async) = 0;
            virtual void moveAbsoluteWait(Basda::DeviceAsync & =
                                              Basda::Device::s_async) = 0;

            /**
             * @brief All motors of a device move to the relative position.
             *
             * @param _positions a vector containing the relative target position
             *                   in steps. The order of the points is corresponding
             *                   to the order of the motor names in the property
             *                   tree (alphabetical).
             * @param _async the call back object
             *
             * @todo change std::map<std::string, double> to Nice::NPoint
             */
            virtual void moveRelative(const Nice::NPoint &_positions,
                                      Basda::DeviceAsync &_async =
                                              Basda::Device::s_async) = 0;
            virtual void moveRelativeStart(const Nice::NPoint &_positions,
                                      Basda::DeviceAsync & =
                                              Basda::Device::s_async) = 0;
            virtual void moveRelativeWait(Basda::DeviceAsync & =
                                              Basda::Device::s_async) = 0;


            /**
             * @brief This method is deprecated and will be exchanged with
             *        getDeviceencoderPosition()
             */
            virtual Nice::NPoint getPosition()=0;

            /**
             * @brief Returns the current device internal position encoder
             *        of all motors.
             *
             * Some devices, like the MoCon, have an internal software
             * encoder/position counter, which is independent from the incremental
             * and absolute encoder.
             *
             * @return the current positions of all motors in steps. The order
             *         of the points is corresponding to the order of the motor
             *         names in the property tree (alphabetical).
             */
            virtual Nice::NPoint getDeviceEncoderPosition()=0;

            /**
             * @brief Returns the current absolute encoder positions of all motors.
             *
             * The current position of all motors might be stored in the property
             *
             * - VAR.POSITION.ABSOLUTE_ENCODER
             *
             * @return the current positions of all motors in steps. The order
             *         of the points is corresponding to the order of the motor
             *         names in the property tree (alphabetical).
             */
            virtual Nice::NPoint getAbsoluteEncoderPosition()=0;

            /**
             * @brief Returns the current incremental encoder positions of all motors.
             *
             * The current position of all motors might be stored in the property
             *
             * - VAR.POSITION.INCREMENTAL_ENCODER
             *
             * @return the current positions of all motors in steps. The order
             *         of the points is corresponding to the order of the motor
             *         names in the property tree (alphabetical).
             */
            virtual Nice::NPoint getIncrementalEncoderPosition()=0;

            /**
             * @brief Change the absolute position of all motors.
             *
             * The motor will not move, only the absolute position will be set.
             *
             * @param _position a vector with the position of all motors
             */
            virtual void setPosition(const Nice::NPoint &_position)=0;

            /**
             * @brief Returns the current velocity of all motors.
             *
             * The current velocity of all motors might be stored in the property
             *
             * - VAR.VELOCITY
             *
             * @return a vector with the current velocity of all motors
             */
            virtual Nice::NPoint getVelocity() = 0;

            /**
             * @brief Returns the basic settings of all motors.
             *
             * Depending of the implementation the basic settings could be:
             * motion profile, revolution, velocity, acceleration, deceleration
             * and jerk. Each basic settings is stored in a sequence.
             *
             * @return a sequence of all motors with a sequence of all basic settings
             */
            virtual Nice::SeqSeqDouble getBasicSettings() = 0;

            /**
             * @brief Set the velocity configuration parameter of all motors.
             *
             * @param _velocity a vector with the velocity of all motors. The
             *        order of the velocities is corresponding to the order of
             *        the motor names in the property tree (alphabetical).
             */
            virtual void setVelocitySetting(const Nice::NPoint &_velocity) = 0;

            /**
             * @brief Set the acceleration configuration parameter of all motors.
             *
             * @param _acceleration a vector with the acceleration of all motors. The
             *        order of the accelerations is corresponding to the order of
             *        the motor names in the property tree (alphabetical).
             */
            virtual void setAccelerationSetting(const Nice::NPoint &_acceleration) = 0;

            /**
             * @brief Set the deceleration configuration parameter of all motors.
             *
             * @param _deceleration a vector with the deceleration of all motors. The
             *        order of the decelerations is corresponding to the order of
             *        the motor names in the property tree (alphabetical).
             */
            virtual void setDecelerationSetting(const Nice::NPoint &_deceleration) = 0;

            /**
             * @brief Set the jerk configuration parameter of all motors.
             *
             * @param _jerk a vector with the jerk of all motors. The
             *        order of the jerks is corresponding to the order of
             *        the motor names in the property tree (alphabetical).
             */
            virtual void setJerkSetting(const Nice::NPoint &_jerk) = 0;

            /**
             * @brief Returns the current limit switch state of all motors in a vector.
             *
             * @return true if on of the motors is in the limits. The order of
             *         the limits is corresponding to the order of the motor names
             *         in the property tree (alphabetical).
             */
            virtual bool isAtLimit(Nice::NPoint &_limit)=0;

            /**
             * @brief This method indicate whether the motor is at the home position.
             *
             * @param _atHome each flag in that Bitset indicate whether the motor
             *        is at home (true - motor is at home;). The order of the
             *        bits is corresponding to the order of the motor names in
             *        the property tree (alphabetical).
             */
            virtual bool isAtHome(Nice::BitSet &_atHome)=0;

            /**
             * @brief Returns the motion flag of all motors in a vector.
             *
             * @return true if at least one motor is in motion. The order of the
             *         bits is corresponding to the order of the motor names in
             *         the property tree (alphabetical).
             */
            virtual bool isMoving(Nice::BitSet &_motion)=0;

//            virtual void checkMotionError()=0;

            virtual void setLinkTimeout(Nice::Time _tcpConnectionTimeout)=0;
            virtual Nice::SeqTime getLinkTimeout()=0;
            virtual Nice::NPoint getReferenceTime()=0;
            virtual Nice::NPoint getCurrentTime()=0;

            virtual void startExternalProfile(const Nice::Date &_date,
                                              const Nice::NPoint &_positionSequence,
                                              unsigned int _frequency,
                                              unsigned int _samplesPerSegment,
                                              unsigned int _faultTolerance,
                                              Basda::DeviceAsync &_async =
                                                      Basda::Device::s_async)=0;

            virtual void shinsStartExternalProfile(const Nice::Date &_date,
                                                   const Nice::NPoint & _positionSequence,
                                                   unsigned int _frequency,
                                                   unsigned int _samplesPerSegment,
                                                   unsigned int _faultTolerance,
                                                   Basda::DeviceAsync & _async = Basda::Device::s_async)=0;

            virtual int shinsGetStartingRamp(const Nice::NPoint & _positionSequence, double time_interval_microseconds)=0;
            virtual int sharkDerotatorBacklashProcedure(int procedure)=0;

            virtual void changeExternalProfile(const Nice::Date &_date,
                                               const Nice::NPoint &_positionSequence)=0;

            virtual void prepareExternalProfile(const Nice::SeqSeqInt &_segmentsX,
                                            const Nice::SeqSeqInt &_segmentsY,
                                            Basda::DeviceAsync &_async =
                                                    Basda::Device::s_async) = 0;
            virtual void startExternalProfile(Basda::DeviceAsync &_async =
                                                    Basda::Device::s_async) = 0;

            virtual void shinsStartExternalProfile(Basda::DeviceAsync &_async = Basda::Device::s_async) = 0;
//            virtual bool uploadExternalProfileBlock()=0;

            /**
             * @brief The external profile will be configured and the external
             *        profile will uploaded to the MoCon.
             *
             * @param _positionSequence a vector with the profile for each motor
             * @param _timeResolution segmentLength / samplesPerSegment
             * @param _samplesPerSegment samples per segment
             * @param _maxError the maximal error that the external profile can
             *                  have after the conversion to a MoCon profile
             * @param _asynch the call back object
             */
//            virtual void setExternalProfile(const Nice::SeqNPoint & _positionSequence,
//                                            double _timeResolution,
//                                            unsigned int _samplesPerSegment,
//                                            unsigned int _maxError,
//                                            Basda::DeviceAsync &_asynch)=0;
//
//            virtual void setExternalProfile(const Nice::NPoint & _positionSequence, double _timeResolution, unsigned int _samplesPerSegment, unsigned int _maxError, Basda::DeviceAsync &_asynch)
//            {
//               setExternalProfile(Nice::SeqNPoint(1, _positionSequence), _timeResolution, _samplesPerSegment, _maxError, _asynch);
//            }
//
//
//            /**
//             * @brief This method starts the external profile at a certain data.
//             *
//             * The external profile can be stopped with the stop method.
//             *
//             * @param _date a vector with the start date of all motors. The order
//             *              of the dates is corresponding to the order of the motor
//             *              names in the property tree (alphabetical). Use the
//             *              constant Basda::STAY if the motor should move.
//             * @param _asynch the call back object
//             */
//            virtual void startExternalProfile(Nice::Date _date,
//                                              Basda::DeviceAsync &_asynch)
//            {
//                startExternalProfile(Nice::SeqDate(1, _date), _asynch);
//            }
//            virtual void startExternalProfile(Nice::SeqDate _date,
//                                              Basda::DeviceAsync &_asynch)=0;
//
//            virtual void pauseExternalProfile()=0;
//
//            virtual void stopExternalProfile()=0;
            virtual Nice::SeqBitSet getPositionSwitchStatus()=0;

            virtual void dumpStatusInformation(Basda::DeviceAsync &_asynch,
                                               bool _compareParameter = false) = 0;

            virtual void setCollision(Nice::SeqPoint _collisions) = 0;
    };

    namespace Command
    {
        enum Constant
        {
            OPEN,
            CLOSE,
            RESET,
            INIT,
            DEINIT,
            ACTIVATE,
            DEACTIVATE,
            MOVETOHOME,
            MOVETOHOMESINGLE,
            MOVEOUTOFLIMIT,
            MOVETOLIMIT,
            MOVETOPOSITIVELIMIT,
            MOVETONEGATIVELIMIT,
            MOVEABSOLUTE,
            MOVERELATIVE,
            PREPAREEXTERNALPROFILE,
            STARTEXTERNALPROFILE,
            SHINSSTARTEXTERNALPROFILE,
            SHINSBACKLASHPROCEDURE,
            DUMPSTATUSINFORMATION,
            CHAT,
            DONTPARSE,
            RECEIVEDATARAW
        };

        namespace Action
        {
            const std::string OPEN   = "Open Device";
            const std::string CLOSE  = "Close Device";
            const std::string RESET  = "Reset Device";
            const std::string INIT   = "Init Device";
            const std::string DEINIT = "Deinit Device";
            const std::string ACTIVATE = "Activate Device";
            const std::string DEACTIVATE = "Deativate Device";
            const std::string MOVETOHOME = "Moving Home";
            const std::string MOVETOHOMESINGLE = "Moving Home Single Motor";
            const std::string MOVEOUTOFLIMIT = "Moving out of Limit";
            const std::string MOVETOLIMIT = "Moving to Limit";
            const std::string MOVETOPOSITIVELIMIT = "Moving to positive Limit";
            const std::string MOVETONEGATIVELIMIT = "Moving to negative Limit";
            const std::string MOVEABSOLUTE = "Moving Absolute";
            const std::string MOVERELATIVE = "Moving Relative";
            const std::string PREPAREEXTERNALPROFILE = "Prepare External Profile";
            const std::string STARTEXTERNALPROFILE = "Start External Profile";
            const std::string SHINSSTARTEXTERNALPROFILE = "Shins Start External Profile";
            const std::string SHINSBACKLASHPROCEDURE = "Shins Start Backlash Procedure";
            const std::string DUMPSTATUSINFORMATION = "Dump Status Info";
            const std::string CHAT = "chat";
            const std::string DONTPARSE = "Don't Parse!";
            const std::string RECEIVEDATARAW = "Receive Raw Data";
        }
    }


    /**
     * @brief Definition of the smart pointer of this class.
     */
    typedef IceUtil::Handle<MoccaNDevice> MoccaNDevicePtr;
}
