#pragma once

/* $Header: https://svn.mpia.de/gulli/TwiceAsNice/branch/unstable/TwiceAsNice/basda-mocca/src/device/mpia/moconDummy/NDeviceDummy.h 15131 2018-02-16 20:48:59Z mathar $
*/

#include <config.h>
#include <Nice/Util/Completion.h>
#include <Basda/Mocca/NDevice.h>
#include "moconDummy/ModuleDummy.h"

namespace Basda
{
    class MoConNDeviceDummy : public Basda::MoccaNDevice
    {
        public:
            MoConNDeviceDummy(const Nice::JointPtr & _config);
            virtual ~MoConNDeviceDummy();

            //! open device
            virtual void open();

            //! close device
            virtual void close();

            //! reset the device
            virtual void reset(Basda::DeviceAsync & = Basda::Device::s_async);

            //! version
            virtual void version();

            //! init the device
            virtual void init(Basda::DeviceAsync & = Basda::Device::s_async);

            //! deinit the device
            virtual void deinit(Basda::DeviceAsync & = Basda::Device::s_async);

            //! activate the device
            virtual void activate(DeviceAsync & = Basda::Device::s_async);

            //! deactivate the device
            virtual void deactivate(DeviceAsync & = Basda::Device::s_async);

            /**
             * @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 _cardNo     the ID of the MPIA electronic card
             * @param _moduleNo   the ID of the device module
             * @param _selectID   the sub command ID
             * @param _parameter  the parameter as string
             * @param _numberOfLines the number of lines that might be read
             *
             * 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
             */
            virtual void chat(std::string _commandID,
                    std::string _cardNo,
                    std::string _moduleNo,
                    std::string _selectID,
                    std::string _parameter,
                    std::string _numberOfLines,
                    DeviceAsync & = Basda::Device::s_async);

            /**
             * @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.
             *
             * @return the raw response string
             *
             * @see chat
             */
            virtual std::string dontParse(DeviceAsync & = Basda::Device::s_async);

            /**
             * @brief This method was requested by Lorenzo Busoni (lbusoni@arcetri.astro.it 02.03.2012)
             *
             * It supports a direct communication with the MoCon without to parse
             * the response.
             * 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 which is defined in the CommandIDs.h
             * @param _cardNo      the card number
             * @param _moduleNo    the module number
             * @param _ret         the allocated space for the returned data
             * @param _numberOfLines  optional: This parameter limitates how many
             *                                   data should be read from the Ethernet
             *                                   buffer. All data are read if the value is -1.
             *
             * @return the return parameter is stored in _ret parameter.
             *
             * @see chat
             */
            virtual std::string receiveDataRaw(std::string _commandID,
                    std::string _cardNo,
                    std::string _moduleNo,
                    std::string _numberOfLines,
                    DeviceAsync & = Basda::Device::s_async);

            virtual bool isReachable();

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

            /**
             * @brief All motors of a device move to the absolute position.
             *
             * @param _positions a vector containing the motor name and the
             *                   absolute target position in millimeter
             */
            virtual void moveAbsolute(const Nice::NPoint &_positions,
                    Basda::DeviceAsync &_async = Basda::Device::s_async)
            {
                moveAbsoluteStart(_positions, _async);
                moveAbsoluteWait(_async);
            }
            virtual void moveAbsoluteStart(const Nice::NPoint &/*_positions*/,
                    Basda::DeviceAsync & = Basda::Device::s_async);
            virtual void moveAbsoluteWait(Basda::DeviceAsync & =
                    Basda::Device::s_async);

            /**
             * @brief All motors of a device move to the relative position.
             *
             * @param _positions a vector containing the motor name and the
             *                   relative target position in millimeter
             */
            virtual void moveRelative(const Nice::NPoint &_positions,
                    Basda::DeviceAsync &_async = Basda::Device::s_async)
            {
                moveRelativeStart(_positions, _async);
                moveRelativeWait(_async);
            }
            virtual void moveRelativeStart(const Nice::NPoint &/*_positions*/,
                    Basda::DeviceAsync &_async = Basda::Device::s_async);
            virtual void moveRelativeWait(Basda::DeviceAsync & =
                    Basda::Device::s_async);

            /**
             * @brief All motors of a device move to the home position.
             */
            virtual void moveToHome(Basda::DeviceAsync &_async =
                    Basda::Device::s_async)
            {
                moveToHomeStart(_async);
                moveToHomeWait(_async);
            }
            virtual void moveToHomeStart(Basda::DeviceAsync & =
                    Basda::Device::s_async);
            virtual void moveToHomeWait(Basda::DeviceAsync & =
                    Basda::Device::s_async);

            virtual void moveToHomeSingle(int _motor,
                    Basda::DeviceAsync &_async = Basda::Device::s_async)
            {
                moveToHomeSingleStart(_motor, _async);
                moveToHomeSingleWait(_async);
            }
            virtual void moveToHomeSingleStart(int _motor,
                    Basda::DeviceAsync & = Basda::Device::s_async);
            virtual void moveToHomeSingleWait(Basda::DeviceAsync & =
                    Basda::Device::s_async);

            /**
             * @brief All motors of a device move to the positive limit switch.
             */
            virtual void moveToLimit(const Nice::NPoint &_positions,
                    Basda::DeviceAsync &_async = Basda::Device::s_async)
            {
                moveToLimitStart(_positions, _async);
                moveToLimitWait(_async);
            }
            virtual void moveToLimitStart(const Nice::NPoint &_positions,
                    Basda::DeviceAsync & = Basda::Device::s_async);
            virtual void moveToLimitWait(Basda::DeviceAsync & =
                    Basda::Device::s_async);

            virtual void moveToPositiveLimit(Basda::DeviceAsync &_async =
                    Basda::Device::s_async)
            {
                moveToPositiveLimitStart(_async);
                moveToPositiveLimitWait(_async);
            }
            virtual void moveToPositiveLimitStart(Basda::DeviceAsync &_async =
                    Basda::Device::s_async);
            virtual void moveToPositiveLimitWait(Basda::DeviceAsync &_async =
                    Basda::Device::s_async);

            virtual void moveToNegativeLimit(Basda::DeviceAsync &_async =
                    Basda::Device::s_async)
            {
                moveToNegativeLimitStart(_async);
                moveToNegativeLimitWait(_async);
            }
            virtual void moveToNegativeLimitStart(Basda::DeviceAsync &_async =
                    Basda::Device::s_async);
            virtual void moveToNegativeLimitWait(Basda::DeviceAsync &_async =
                    Basda::Device::s_async);

            /**
             * @brief All motors of a device stop moving.
             */
            virtual void stop();

            /**
             * @brief All motors of a device abort the current movement.
             */
            virtual void abort();

            /**
             * @brief Returns the current positions of all motors.
             *
             */
            virtual Nice::NPoint getPosition();
            virtual Nice::NPoint getDeviceEncoderPosition();
            virtual Nice::NPoint getAbsoluteEncoderPosition();
            virtual Nice::NPoint getIncrementalEncoderPosition();

            virtual void setPosition(const Nice::NPoint &_position);

            /**
             * @brief Returns the current velocity of all motors.
             *
             */
            virtual Nice::NPoint getVelocity();

            /**
             * @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();

            /**
             * @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);

            /**
             * @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);

            /**
             * @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);

            /**
             * @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);

            /**
             * @brief Returns the current limit switch state of all motors.
             */
            virtual bool isAtLimit(Nice::NPoint &);

            /**
             * @brief Returns the motion flag of all motors.
             */
            virtual bool isMoving(Nice::BitSet &);

            virtual bool isAtHome(Nice::BitSet &);

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

            virtual void checkMotionError();

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

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

            void startExternalProfile(Basda::DeviceAsync &_async = Basda::Device::s_async);

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

            void shinsStartExternalProfile(Basda::DeviceAsync &_async = Basda::Device::s_async);
            virtual int shinsGetStartingRamp(const Nice::NPoint & _positionSequence, double time_interval_microseconds){return 0;};
            virtual int sharkDerotatorBacklashProcedure(int procedure){return 0;};

            /**
             * @brief This method wait until the next trigger has tobe raised.
             *
             * @param _date the absolute time for the next trigger
             */
            void waitForNextTrigger(Nice::Date _date,
                                    Basda::DeviceAsync &_async =
                                            Basda::Device::s_async);
            void shinsWaitForNextTrigger(Nice::Date _date, Basda::DeviceAsync &_async = Basda::Device::s_async);

            /**
             * @brief This method fit, validates and buffers the external profile.
             *
             * @param _date - the start date when the new trajectory should start
             * @param _positionSequence - the position sequence in steps
             */
            virtual void changeExternalProfile(const Nice::Date &_date,
                                               const Nice::NPoint &_positionSequence);

            virtual Nice::SeqBitSet getPositionSwitchStatus();

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

            virtual Nice::Completion getProgress();

            /*
             * @brief This method allows to use a different kind of Chat class.
             *
             * This method is necessary to test the device with a MoCon simulator.
             */
            void setChatType(std::map<std::string, Nice::ChatLinePtr> _chat);

            /*
             * This method sets a collision to simulate different situations.
             */
            void setCollision(Nice::SeqPoint _collisions);

            /*
             * This methods disables some bit of the filter wheel reference positions.
             * It is needed to simulate broken switches.
             */
            void setBrokenSwitches(Nice::BitSet _brokenPositionSwitchMask, int _fromPosition, int _toPosition);

            /*
             * This method set the current position of the filter wheel to
             * simulate unknown filter wheel positions.
             */
            void setWheelPositionOffset(int _positionOffset);

            // to avoid compiler warnings :)
            virtual Nice::JointPtrVector description() {return Nice::JointPtrVector();};

        protected:

            void setIsReachable(const bool _isReachable);

            /**
             * @brief This method fit, validates and buffers the external profile.
             *
             * @param _date - the start date when the trajectory should start
             * @param _positionSequence - the position sequence in steps
             */
            void generateExternalProfile(const Nice::Date &_date,
                                               const Nice::NPoint &_positionSequence);

            void startTimer(double _distance);

            void waitInternal(const std::string _cmd,
                    Basda::DeviceAsync & _async);

            /**
             * @brief Checks if the motion is complete.
             *
             * This method collect the MOTION_COMPLETE flags of all motors.
             *
             * The following property will indicate the motion completeness
             *
             * - VAR.MOTION_COMPLETE
             *
             * @param _motion returns which motor is in motion
             * @return true if all motor have completed its motion
             */
            bool isMotionComplete(Nice::BitSet &_motion);

            Nice::Date niceDateNow();

            std::map<std::string, MoConModuleDummyPtr> m_module;

            Nice::NPoint m_limit;

            unsigned int m_externalProfileSize;

            /**
             * @brief Keeps the last external profile info value to reduce the
             *        number of log messages.
             */
            int m_externalProfileInfo;

            bool m_isReachable;

            // these members are needed to compute the percentage of the completion
            Nice::Timer m_timer;
            Nice::Time m_duration;

            //Deprecated!
            std::string m_cardNo;
            std::string m_moduleNo;
            std::string m_commandID;
//            bool m_inMotion;
            Nice::Mutex m_mutex;

            // following members and methods should be protected because
            // there are needed in the NDeviceDummyMock class
            // to test the wheel service
//        private:
            void createInstances();

            void addProperties();

            /**
             * @brief This method creates an ID based on the connection address,
             *        card number and module number separated by a colon to
             *        identify the module/card object in a map.
             *
             * @param _address  the connection string
             * @param _cardNo   the card number
             * @param _moduleNo the module number
             */
            std::string createConnectionID(const std::string &_address,
                                           const unsigned int _cardNo,
                                           const unsigned int _moduleNo);


            /**
             * @brief This member keeps all chat objects. The original
             *        Nice::Chat::Line object can be exchanged with a
             *        Chat::LineMock object to simulate the MoCon behavior.
             *
             */
            std::map<std::string, Nice::ChatLinePtr> m_chat;

            Nice::Time m_tcpConnectionTimeout;

            Nice::DateDummyPtr m_niceDateDummy;
    };

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