// Motor.h: interface for the CMotor class.
//
//////////////////////////////////////////////////////////////////////
#pragma once

#include <memory>
#include <string>
#include <vector>

#include "HardwareIni.h"

#ifdef BUILD_IODLL
#define IODLLCLASS __declspec(dllexport)
#else
#define IODLLCLASS __declspec(dllimport)
#endif

// Motion card Type
#define PCI_8134            0
#define PCI_8134_STR        "8134"
#define PCI_8164_ADLINK     1
#define PCI_8164_ADLINK_STR "8164"
#define PCI_GALIL           2
#define PCI_GALIL_STR       "Galil"

// ETEL
#define PCI_ETEL     3
#define PCI_ETEL_STR "ETEL"

#define PCI_APS_ADLINK     4 // master driver for many different supported cards, eg 7856, 8256
#define PCI_APS_ADLINK_STR "ADLINK_APS"

#define PCI_7856_MOTIONNET_ADLINK 5

#define ACS_MOTOR_ID   6
#define ACS_MOTOR_STR  "ACS"
#define ACS_MUTEX_NAME "acs_mutex"

// advantech ethercat
#define PCI_ETHERCAT_MASTER_ADVANTECH     7 // master driver for many different supported cards, eg 1203
#define PCI_ETHERCAT_MASTER_ADVANTECH_STR "ADVANTECH_ETHERCAT"

#define PCI_1203_ETHERCAT_MOTION_ADVANTECH 8
//

#define MAX_MOTION_CONTROLLER_TYPE 9

#define MAX_MC_MOTOR 64 // Max motor per machine
#define MAX_MOTOR    50
// #define MAX_MOTOR_MODULE_ARRAY  MAX_MOTOR
// #define MAX_IO_MODULE_ARRAY     75
// #define MAX_MOTOR_POS           30

#define MOTOR_STEPPER                  0
#define MOTOR_STEPPER_ENC              1
#define MOTOR_SERVO                    2
#define MOTOR_SERVO_REVERSE            3
#define MOTOR_STEPPER_WITH_INP         4
#define MOTOR_STEPPER_WITH_ENC_AND_INP 5

//  created more motor types to
// handle Adlink motor control to use only internal counter
#define MOTOR_STEPPER_ENC_INTERNAL              6
#define MOTOR_STEPPER_WITH_ENC_AND_INP_INTERNAL 7

#define MOTOR_STEPPER_REVERSE_ENC_INTERNAL              8
#define MOTOR_STEPPER_WITH_REVERSE_ENC_AND_INP_INTERNAL 9

#define MOTOR_PULSE_OUTPUT_1P                0 // Out, Dir
#define MOTOR_PULSE_OUTPUT_2P                1 // CW, CCW (+ve = OUT -> CW)
#define MOTOR_PULSE_OUTPUT_4AB_PHASE_LEADING 6 // A Phase, B Phase (+ve = OUT Leading)

// 8134 IO Status
/* ------------ I/O Status Definition ------------------------------------- */
#define pos_limit  0x01
#define neg_limit  0x02
#define psd_switch 0x04
#define nsd_switch 0x08
#define org_limit  0x10
#define idx_switch 0x20
#define alm_switch 0x40
#define svon_sts   0x80
#define rdy_sts    0x100
#define int_sts    0x200
#define erc_sts    0x400
#define inp_sts    0x800
/* ------------ I/O Status Definition ------------------------------------- */

// 8164 IO status
#define Ready_IO     0x0001
#define Alarm_IO     0x0002
#define Pos_Limit_IO 0x0004
#define Neg_Limit_IO 0x0008
#define Org_IO       0x0010
#define Index_IO     0x0200
#define Inp_IO       0x2000
#define Svon_IO      0x4000

// Galil Tell Switch
#define LATCHED            0x01
#define HOME_SENSOR_STATUS 0x02
#define REV_LIMIT_SENSOR   0x04
#define FWD_LIMIT_SENSOR   0x08
#define UNDEFINED          0x10
#define MOTOR_ON           0x20
#define MOTOR_LIMIT_ERROR  0x40
#define MOTOR_IN_MOTION    0x80

// ETEL Inputs
#define ETEL_HOME_SENSOR      0x0002
#define ETEL_NEG_LIMIT_SENSOR 0x0100
#define ETEL_POS_LIMIT_SENSOR 0x0200

// ETEL M60 STATUS
#define ETEL_STATUS_CONTROLLER_POWER_ON  0x1
#define ETEL_STATUS_HOME_COMPLETED       0x4
#define ETEL_STATUS_MOTOR_PRESENT        0x8
#define ETEL_STATUS_MOTOR_IS_MOVING      0x10
#define ETEL_STATUS_MOTOR_IS_IN_POSITION 0x20
#define ETEL_STATUS_MOTOR_ERROR          0x400
#define ETEL_STATUS_MOTOR_DOING_HOMING   0x2000

enum MOTION_STATUS
{
    MOTION_HOMING,
    MOTION_MOVING,
    MOTION_END,
    MOTION_LIMIT_POS_HIT,
    MOTION_LIMIT_NEG_HIT,
    MOTION_LIMIT_HIT,
    MOTION_MTR_OFF,
    MOTION_DRIVER_ALARM,
    MOTION_ERROR,
    MOTION_FAILED_TO_STOP,
    MOTION_MTR_ON,
    MOTION_COMMAND_SUCCESS,
    MOTION_EXCEED_SOFT_LIMIT_ERROR,
    MOTION_FORCE_STOP,
    MAX_MOTION_STATUS,
};

enum HOMING_METHOD
{
    HOME_SENSOR,
    HOME_INDEX,
    HOME_SENSOR_INDEX,
    HOME_BY_LIMIT,
    HOME_BY_LIMIT_THEN_INDEX = 27,
};

struct stMtrPosTeach
{
    std::string strPos{};
    BOOL bTeachEnable{ FALSE };
};

// motor profile data structure
class IODLLCLASS CMtrProfile
{
public:
    std::string m_strMotorName{};
    int m_iMtrNo;      // motor no wrt the type of card used
    int m_iMtrType;    // servo or stepper, with or without encoder feedback
                       //  int     m_iUpr;         // unit per motor revolution
                       //  int     m_iPPR;         // pulse per revolution
                       //  int     m_iEPPR;        // encoder pulse per revolution
    double m_dUpr;     // unit per motor revolution
    double m_dPPR;     // pulse per revolution
    double m_dEPPR;    // encoder pulse per revolution
    int m_iPosDir;     // positive dir is CW or CCW
    int m_iClk;        // OUT/DIR or CW/CCW type
    int m_iHomeMode;   // homing mode
    int m_iHomeDir;    // homing dir
    int m_iIndexLogic; // index or z pulse signal logic from the encoder
    int m_iALM;        // motor driver alarm signal logic
    int m_iINP;        // in-position logic from motor driver
    int m_iPosEL;      // positive limit sensor signal logic
    int m_iNegEL;      // negative limit sensor signal logic
    int m_iORGI;       // home sensor signal logic
    int m_iRDY;        // motor driver Ready signal logic
    int m_iSON;        // motor enable signal logic
    int m_iCLR;        // motor driver clear alarm signal logic

    double m_dConst;          // constant speed, must be double
    double m_dStartUp;        // start speed, must be double
    double m_dCruise;         // max speed, must be double
    double m_dAcce;           // acceleration
    double m_dDec;            // deceleration
    int m_iCardType;          // card type, eg 8134
    double m_dSAcce;          // s-curve profile acceleration
    double m_dSDec;           // s-curve profile deceleration
    double m_dHomeLeaveSteps; // leave steps for homing

    int m_iEncoderType; // type of encoder, 1xAB, 2xAB, 4xAB, CW/CCW

    double m_dPosSoftLimit; // positive Software Limit
    double m_dNegSoftLimit; // negative Software Limit

    int m_iCardNo;

    int m_iMotorPosArrayIndex;

    std::vector<stMtrPosTeach> m_motorPos;
};

class IODLLCLASS CMtrPos
{
public:
    //  default constructor
    CMtrPos();

    //  constructor to initialised current instance motor num
    CMtrPos(int iMtrNo);

    //  destructor
    virtual ~CMtrPos();

    //  get current motor position based on motor number.
    //  if int iMtrNo = -1, return current instance motor position
    int GetCurPos(int iMtrNo = -1);

    //  set total no of motor based on motor number
    void SetNoOfMtr(int iNoOfMtr);

    enum
    {
        INVALID    = -1,
        NO_POS_SET = -1,
    };
    bool IsValidMotor(int iMtrNo);

protected:
    //  set the current position of motor based on motor number
    //  if int iMtrNo = -1, return set current instance motor position
    void SetCurPos(int iPos, int iMtrNo = -1);

private:
    static std::vector<int> m_vecMtrCurPos; // array to keep track all motor current position
    static int m_iNoOfInstance;             // keep track no of instance
    static int m_iNoOfMtr;                  // keep track of total number of motor

    int m_iMtrNo; // keep track motor number

    enum
    {
        NO_SPECIFIC_MOTOR = -1,
    };
};

class IODLLCLASS CMtrOnOff
{
public:
    //  default constructor
    CMtrOnOff();

    //  constructor to initialised current instance motor num
    CMtrOnOff(int iMtrNo);

    virtual ~CMtrOnOff();

    //  get current motor On/Off based on motor number.
    //  if int iMtrNo = -1, return current instance motor position
    bool GetCurMtrOnOff(int iMtrNo = -1);

    //  set total no of motor based on motor number
    void SetNoOfMtr(int iNoOfMtr);

    enum
    {
        INVALID      = -1,
        NO_STATE_SET = -1,
    };

    int GetMotorCount();
    bool IsValidMotor(int iMtrNo);

protected:
    //  set the current On/Off of motor based on motor number
    //  if int iMtrNo = -1, return set current instance motor On/Off
    void SetCurMtrOnOff(bool bOnOffState, int iMtrNo = -1);

private:
    static std::vector<bool> m_vecMtrCurOnOff; // array to keep track all motor current On/Off States
    int m_iMtrNo;                              // keep track motor number
    static int m_iNoOfInstance;                // keep track no of instance

    static int m_iNoOfMtr; // keep track of total number of motor

    enum
    {
        NO_SPECIFIC_MOTOR = -1,
    };
};

class CMotorBase;

// applicable to GALIL pulse stretcher
enum GALIL_PULSE_TYPE
{
    STRETCH_NON,   // 600 ns
    STRETCH_TYPE1, // 2ms
};

class IODLLCLASS CMotor : public CCard // public CMtrPos, CCard, CMtrOnOff, CMyWait
{
public:
    int GetCurPos();

    int SetMultipleTrigger(double dStartPosition, double dStopPosition, double dIntervalDistance, int nNoOfTrigger, int nCmpSource = 1);

    int SetMultipleTrigger(double* pdPosition, int nNoOfTrigger, int nCmpSource = 1);

    int SMoveContinuously(bool bCW = true);

    int MoveContinuously(bool bCW = true);

    // ************************************************************
    // Applicable to Galil Axis - STARTS
    // ************************************************************
    // currently used by Galil only
    enum INTERRUPT_CODE
    {
        NEG_LIMIT_HIT = 1,
        POS_LIMIT_HIT,
        HOME_MOVE_COMPLETE,
    };

    std::string GetVariable(std::string csVariableName);

    int StopProgramAndMotion();

    int UpdateProfile();

    int SendCommand(std::string csCommand);
    // ************************************************************
    // Applicable to Galil Axis - ENDS
    // ************************************************************

    // ************************************************************
    // Methods to enquire other motors status - STARTS
    // ************************************************************
    double GetOtherAxisPosition(CMtrProfile mtrProfile);

    // Check other axis home sensor
    BOOL IsHomed(CMtrProfile mtrProfile);

    // Get other axis motion IO status
    int GetMotionIOStatus(CMtrProfile mtrProfile);

    // Get Other axis servo ready status
    BOOL IsServoReady(CMtrProfile mtrProfile);

    // Get Other axis Alarm status
    BOOL IsServoAlarm(CMtrProfile mtrProfile);

    // Get Other axis negative limit status
    BOOL IsNegLmtHit(CMtrProfile mtrProfile);

    // Get Other axis positive limit status
    BOOL IsPosLmtHit(CMtrProfile mtrProfile);

    // Get Other axis positive limit status
    bool IsMtrOn(CMtrProfile mtrProfile);

    // ************************************************************
    // Methods to enquire other motors status - ENDS
    // ************************************************************

    int SetConversionFactor(double dUPR, double dPPR, double dEPPR);

    // currently only support 8134
    int SimultaneousMove(int nNoOfAxis, CMtrProfile* mtrProfile, double* dPosition, double* dStartVel, double* dMaxVel, double* dAcc, int nMode = 0);

    // currently only support 8134A
    bool StopSimultaneousMove(int nFirstAxisNo);

    int SetVirtualHomePosition(double dVirtualHomePosition);

    int ResetSingleTrigger(double dPosition = 0.0);

    void UpdatePosition(int nPos);

    void ResetRequiredMotionEvents();

    double ConvertToPulse(double dDistance);

    bool IsEnableIntCheck();
    void DisableIntCheck();
    void EnableIntCheck();

    // specify the pulse (time) stretch type used for galil card

    // applicable to GALIL, ADLINK 8164
    int SetSingleTrigger(double dPosition, int nCmpSource = 1, int nPulseStretch = STRETCH_NON);

    // bool HomingByLimit();

    double GetNegativeSoftLimit();

    double GetPositiveSoftLimit();

    void SetNegativeSoftLimit(double dNegSoftLimit);

    void SetPositiveSoftLimit(double dPosSoftLimit);

    int SetCommandPosition(double dPosition);

    // return: success = MOTION_END; error = MOTION_ERROR; Force Stop = MOTION_FORCE_STOP
    DWORD WaitHomeDone();

    int GetMotionIOStatus();

    BOOL IsServoReady();

    BOOL IsServoAlarm();

    BOOL IsNegLmtHit();

    BOOL IsPosLmtHit();

    BOOL IsHomed();

    BOOL IsIndex();

    bool IsMtrOn();

    double GetCommandPosition();

    void UpdatePosition();

    HANDLE GetMotionInProgressTrigger();

    HANDLE GetHomeMotionDoneTrigger();

    HANDLE GetMotionDoneTrigger();

    double GetPosErr();

    // forced set the current position
    int SetPosition(double dPosition);

    // return the current motor position
    double GetPosition();

    // move relative distance
    int MoveRel(double dDistance, int nPos = -1); // default NO Position

    // added new absolute move mode
    // move absolute
    int MoveAbs(double dPosition, int nPos = -1, int nMode = 0); // default NO Position

    // move relative distance with s-curve profile
    int SMoveRel(double dDistance, int nPos = -1); // default NO Position

    // added new absolute move mode
    // move absolute with s-curve profile
    int SMoveAbs(double dPosition, int nPos = -1, int nMode = 0); // default NO Position

    // added new absolute move mode
    //  move motor based on position array with s-curve profile
    int SMove(int iPos, int nMode = 0);

    // wait for motion stop based on pulse send out or in-position signal
    // call this function after calling a move operation
    // return: success = MOTION_END; error = MOTION_ERROR; Force Stop = MOTION_FORCE_STOP
    // positive limit = MOTION_LIMIT_POS_HIT; negative limit = MOTION_LIMIT_NEG_HIT
    DWORD WaitMotionDone(DWORD dwTimeout = 1000);

    //  constructor to initialised motor number, number of position and motor position array
    //  CMotor(int iMtrNo, int iNoOfPos, double *dpPos);
    //  constructor to initialised motor number, number of position and motor position array and motor profile
    //  constructor to initialised motor position array and motor profile
    CMotor(const CMtrProfile& mtrProfile, double* dpPos);
    virtual ~CMotor(); // destructor

    // added new absolute move mode
    //  move motor based on position array
    int Move(int iPos, int nMode = 0);

    //  home motor, normal or use limit sensor as home. Whether to reset position tracking
    int Home(DWORD dwTimeout, bool bResetPositionTrack = true, bool bByLimit = false);

    void EnableResetPositionDuringHoming(bool bResetPositionDuringHoming);

    //  stop motor with decceleration
    int DecStop();

    //  Emergence stop motor
    int EmerStop();

    //  On/Off motor
    int OnMtr(bool bOn);

    int SetConst(double dConst);

    int SetStartUp(double dStartUp, bool bHomeSlow = false);

    int SetCruise(double dCruise, bool bHomeSlow = false);

    int SetAcce(double dAcce, bool bHomeSlow = false);

    int SetDec(double dDec, bool bHomeSlow = false);

    int SetSAcce(double dSAcce);

    int SetSDec(double dSDec);

    // This method is used to reset alarm via MotionNet
    int ResetAlarm(bool bReset);

    // These methods only applies to ETEL Gantry Mode
    int GantryEnable(int nNoOfAxis, CMtrProfile* mtrProfile);

    int GantryDisable(int nNoOfAxis, CMtrProfile* mtrProfile);

    // Method to force a compare output pulse
    int TriggerCompareOutput();

    int SegmentedMotion(int nAxis, const CMtrProfile* pMtrProfile, double* dPositions);
    int SegmentedLine(double* dPositions);
    int SegmentedEnd();
    int SegmentedArc(double* dCenter, double* dFinalPoint, int rotation);

    int MasterSlaveEnable(const CMtrProfile slaveMtrProfile, double offset, double leftBoundary, double rightBoundary);
    int MasterSlaveDisable(const CMtrProfile slaveMtrProfile);

    bool SetFixSpdRange(double dMaxVel, bool bRealWorldUnit = false);
    bool UnFixSpdRange();

    // LERP - Linear Interpolation
    bool StartXYRelMoveLinear(double dDistX, double dDistY, double dStartVel, double dMaxVel, double dAcc, double dDec);
    bool StartZURelMoveLinear(double dDistZ, double dDistU, double dStartVel, double dMaxVel, double dAcc, double dDec);
    bool StartXYSRelMoveLinear(double dDistX, double dDistY, double dStartVel, double dMaxVel, double dAcc, double dDec, double dSAcc, double dSDec);
    bool StartZUSRelMoveLinear(double dDistZ, double dDistU, double dStartVel, double dMaxVel, double dAcc, double dDec, double dSAcc, double dSDec);

private:
    // all the various motion controller objects
    std::shared_ptr<CMotorBase> m_motorBase;
};
