#pragma once

#include "RunTask.h"

#include <set>
#include <deque>
#include <vector>
#include <string>

#include "OutputController.h"
#include "Utility.h"

#include "structure.h"

#include "DllDefines.h"

#include "commondef.h"
#include "MotorPositionParser.h"
#include "MotorSpeedParser.h"

#include "MaintStructs.h"

constexpr auto OPE_STATUS_UPDATE = 100;

enum MODE
{
    MODE_POWER_UP,
    MODE_AUTO,
    MODE_MAINTENANCE,
    MODE_TASK,
    MODE_UNKNOWN
};

enum STATE
{
    STATE_RUN,
    STATE_STOP
};

enum
{
    SLOW_SPEED,
    NORMAL_SPEED
};

// motor profile
enum
{
    HOME_START_SPEED,
    HOME_MAX_SPEED,
    HOME_ACC,
    HOME_DEC,
    HOME_PERCENTAGE,

    SLOW_START_SPEED,
    SLOW_MAX_SPEED,
    SLOW_ACC,
    SLOW_DEC,
    SLOW_PERCENTAGE,

    NORMAL_START_SPEED,
    NORMAL_MAX_SPEED,
    NORMAL_ACC,
    NORMAL_DEC,
    NORMAL_PERCENTAGE,

    SLOW_START_SPEED_LIMIT,
    SLOW_MAX_SPEED_LIMIT,
    SLOW_ACC_LIMIT,
    SLOW_DEC_LIMIT,
    SLOW_PERCENTAGE_LIMIT,

    NORMAL_START_SPEED_LIMIT,
    NORMAL_MAX_SPEED_LIMIT,
    NORMAL_ACC_LIMIT,
    NORMAL_DEC_LIMIT,
    NORMAL_PERCENTAGE_LIMIT,

    SLOW_OVER_VELOCITY,
    NORMAL_OVER_VELOCITY,
    SLOW_SPEED_LIMIT_OVER_VELOCITY,
    NORMAL_SPEED_LIMIT_OVER_VELOCITY,

    MAX_SPEED_PROFILE_ITEMS
};

class MCCTRDLLCLASS CRun : public CRunTask
{
public:
    // new method to inform UI of OPE status/signal
    bool PostOPESignal(unsigned int uinSignal);

    bool IsMaintenanceOperationCompleted();
    void StopMaintenanceOperation();
    void StartMaintenanceOperation();
    bool PostGeneralMessageToGUI(WPARAM wParam, LPARAM lParam);
    bool PostMessageToGUI(int nMsg, WPARAM wParam, LPARAM lParam);
    void WarningEx(int nWarningNo, bool bSet = true);
    void UpdateMaintenanceMessage(int nMsg, bool bClearDisplay = false);
    bool IsMaintenanceMode() const;
    bool IsPowerUpMode() const;
    bool IsTaskMode() const;
    bool IsAutoMode() const;
    int  GetMode() const;

    virtual void OnStopComplete();
    // virtual function to allow application to handle some operation upon Reset()
    virtual void OnResetComplete();
    //
    virtual void OnMcStopped();
    //
    void GetModuleEnable(const std::string& csFilename, const std::string& csSectionName, const std::string& csKeyName);
    bool IsModuleEnable() const;
    int  GetMaintenanceMoveProfile() const;
    void MaintenanceCheckMotorComplete();     // request to start check maintenance motor move complete
    void MaintenanceCheckMotorHomeComplete(); // request to start check maintenance motor move complete
    void ResetPowerUpComplete(void);
    void EndCloseLot(void);
    void OnCloseLot(void);
    void CloseLotComplete(void);
    bool IsCloseLot(void);
    bool IsCloseLotComplete(void);

    //  reset close lot event
    void ResetCloseLot();

    void         Warning(int nID, bool bDisplay = false);
    bool         GetCountJam(void) const;
    void         PowerUpRestart(void);
    void         AutoRestart(void);
    bool         IsJamCode(void);
    int          GetWarning(void);
    int          GetJam(void);
    bool         IsWarning(void);
    bool         IsPowerUpComplete(void);
    void         PowerUpComplete(void);
    virtual void InitParameters(void);

    CRun(int nID, const std::string& strName, const std::vector<stModule>& modules, const std::vector<int>& sequenceModuleNo);
    virtual ~CRun();

    inline std::string GetName(void) const
    {
        return m_strName;
    }

    inline int GetID(void) const
    {
        return m_nModuleId;
    }

    virtual bool PowerUpOperation(void);
    virtual bool TaskOperation(void);
    virtual bool AutoOperation(void);
    virtual bool MaintenanceOperation(void);

    void OnPowerUp(void);
    void OnStop(void);
    void OnJamReset(void);
    void OnAuto(void);
    void OnTask(void);
    bool IsStop(void);
    bool IsJam(void);
    bool GetWarningClear(void);

    //  get warning code
    int GetWarningCode();

    // Operations
    void Jam(int nJamID, bool bCount = true); // default count in jam

    // Run state
    int GetState();

    void InMaintenance();                  // this function will be executed in Maintenance Mode
    void OnMaintenance(bool bMaintenance); // call this function to set to Maintenance Mode

    void MaintenanceHomeMotor(int iMaintModuleNo, int iMotorNo); // Function to home motor in Maintenance Mode
    void MaintenanceMoveMotor(int iMaintModuleNo, int iMotorNo, int iPos, double dPos,
        int iSpeed);                                                     // Function to Move motor in Maintenance Mode
    void MaintenanceStopMotor(int iMaintModuleNo, int iMotorNo);         // Function to Stop motor in Maintenance Mode
    void MaintenanceOutput(int iMaintModuleNo, int iOutputNo, bool bOn); // Function to trigger output

    //  Function to turn on/off motor in manitenance Mode
    void MaintenanceMotorOnOff(int iMaintModuleNo, int iMtrNo, bool bOn);

    bool IsMaintenanceHomeMotor() const;             // Function to check home motor in maintenance mode
    bool IsMaintenanceMoveMotorHomeComplete() const; // Function to check motor move complete in maintenance mode

    bool IsMaintenanceMoveMotor() const;         // Function to check move motor in maintenance mode
    bool IsMaintenanceMoveMotorComplete() const; // Function to check motor move complete in maintenance mode

    bool IsMaintenanceStopMotor() const; // Function to check stop motor in maintenance mode
    bool IsMaintenanceOutput() const;    // Function to trigger output in maintenance mode
    bool IsMaintenanceOnOffMtr() const;  // Function to check turn on/off motor in maintenance mode
                                         //  check if all maintenance operation completed
    bool IsMaintOperCompleted();

    double GetMaintenanceMoveMotorPos() const; // Function to get Move Motor Position in maintenance mode

    //  Function to get Move Motor Position in maintenance mode
    int GetMaintenanceMoveMotorPosIndex() const;
    int GetMaintenanceMoveMtrNo() const;       // Function to get Motor to move in maintenance mode
    int GetMaintenanceOutputToTrigger() const; // Function to get output number to trigger in maintenance mode

    //  Function to get output on/off status in maintenance mode
    bool GetMaintenanceOutputTriggerState() const;
    int  GetMaintenanceModuleNo() const; // Function to get Maintenance module no
                                         //  Function to get turn motor on/off state during maintenance mode
    bool GetMaintenanceOnOffState() const;

    void ClearMaintStopMtrFlag(); // clear m_bMaintenanceStopMtr flag
    void ClearMaintHmMtrFlag();   // clear m_bMaintenanceHmMtr flag

    void ClearMaintHmMtrCompleteFlag(); // clear m_bCheckMaintenanceHmMtrComplete flag

    void ClearMaintMoveMtrFlag(); // clear m_bMaintenanceMoveMtr flag

    void ClearMaintMoveMtrCompleteFlag(); // clear m_bCheckMaintenanceMoveMtrComplete flag

    void ClearMaintTriggerOutputFlag(); // clear m_bMaintenanceTriggerOutput flag
    void ClearMaintOnOffMtrFlag();      // clear m_bMaintenanceOnOffMtr flag

    //  check if all module no material
    bool IsAllModuleNoMaterial();

    //  runctrl call this function to clear no material flag
    void ClearAllNoMaterial();

    //  check if jam has not been registered before
    bool IsJamNotRegistered();

    //  open lot has been trigger by GUI
    virtual void OnOpenLot();

    //  runctrl call power up restart
    virtual void OnPowerUpRestart();

    //  set CUnitCntArray size. only one module should override this method!
    virtual void SetUnitCntArraySize();

    //  clear auto restart flag
    void ClearAutoRestart();

    //  check if machine has restart
    bool IsAutoRestart();

    //  clear power up restart
    void ClearPowerUpRestart();

    //  check if machine has restart
    bool IsPowerUpRestart() const;

    //  send com data to modules in maintenance mode
    virtual void OnMaintenanceSendCom(LPCTSTR lpcComPort, int iBaudRate, int iDataBits,
        LPCTSTR lpcParity, float fStopBits, LPCTSTR lpcFlowCtrl, LPCTSTR lpcSendMsg);

    //  send open com to modules in maintenance mode
    virtual void OnMaintenanceOpenCom(LPCTSTR lpcComPort, int iBaudRate, int iDataBits,
        LPCTSTR lpcParity, float fStopBits, LPCTSTR lpcFlowCtrl);

    //  send close com to modules in maintenance mode
    virtual void OnMaintenanceCloseCom(LPCTSTR lpcComPort, int iBaudRate, int iDataBits,
        LPCTSTR lpcParity, float fStopBits, LPCTSTR lpcFlowCtrl);

    //  send packet to modules in maintenance mode
    virtual void OnMaintenanceSendPacket(LPCTSTR IpAddress, int iPortNo, LPCTSTR lpcSendMsg);

    //  send connect port to modules in maintenance mode
    virtual void OnMaintenanceConnectPort(LPCTSTR IpAddress, int iPortNo);

    //  send connect port to modules in maintenance mode
    virtual void OnMaintenanceDisconnectPort(LPCTSTR IpAddress, int iPortNo);

    //  get main application window handler
    void GetHwnd(HWND hwnd);

    //  Get General Message
    virtual void GetGenMsg(int iMsg);

    //  CRunCtrl inform all run modules that close lot has completed.
    //  For run modules to do clean up after close lot
    virtual void OnCloseLotCompleted();

    //  Get the previous mode
    int GetPrevMode() const;

    /// For run modules to do clean up Before close applications
    virtual bool OnDestroy()
    {
        return true;
    }

    /// For run modules to do clean up when estop button pressed
    virtual bool OnEStopPressed()
    {
        return true;
    }

    /// For run modules to do necessary actions when curtain sensors acivated
    virtual bool OnCurtainSensorActivated()
    {
        return true;
    }

    /// To inform run module that system is in jog mode
    virtual bool OnSysJogMode(bool bEnable);

    //  determine whether to trace data
    bool m_bTraceData{ false };

    //  determine whether to trace timing data
    bool m_bTraceTimingData{ false };

    //  CRunCtrl inform all run modules that power up has completed.
    //  For run modules to do clean up after system power up
    virtual void OnPowerUpCompleted();

    //  Clear Run Module Warning code
    void ClrWarn();

    void SetMotorPosition(const stRuntimeMotorPosition& runtimeMotorPosition)
    {
        m_runtimeMotorPosition = &runtimeMotorPosition;
    }

    void SetMotorSpeed(const stRuntimeMotorSpeed& runtimeMotorSpeed)
    {
        m_runtimeMotorSpeed = &runtimeMotorSpeed;
    }

    void SetPackageInfo(const void* runtimePackageInfo)
    {
        m_runtimePackageInfo = runtimePackageInfo;
    }

    void SetRecipeInfo(const void* runtimeRecipeInfo)
    {
        m_runtimeRecipeInfo = runtimeRecipeInfo;
    }


protected:
    //  get the module ID
    int GetModuleId();

    //  run module to set home motor in maintenance mode
    void SetMaintenanceHomeMotor(int iMotorNo);

    //  individual module call this function to set to no material
    void SetNoMaterial(bool bNoMaterial);

    //  call this function to return the no material status of module
    bool GetNoMaterialStatus(int iModuleNo);

    //  if repeat jam only display jam, else new jam display and count this jam
    void CallJam(int iJamNo);

    //  clear jam occured before flag
    void ClearJamOccuredBeforeFlag();

    //  Returns warning code in std::set
    std::set<int> GetWarnSet();

    //  Return true if warning has been set before
    bool IsWarningExist(int iWarnCode);

    const stRuntimeMotorPosition& GetRuntimeMotorPosition() { return *m_runtimeMotorPosition; }
    const stRuntimeMotorSpeed&    GetRuntimeMotorSpeed() { return *m_runtimeMotorSpeed; }

    const void* GetRuntimeRecipeInfo() { return m_runtimeRecipeInfo; }
    const void* GetRuntimePackageInfo() { return m_runtimePackageInfo; }

    // event to request motor stop in Maintenance Mode
    CEvt m_evtMaintStop;
    CEvt m_evtMcStop;
    CEvt m_evtEol;
    CEvt m_evtSysJogMode;

    const std::vector<stModule>& m_modules;
    HWND                         m_hwndMainApp{ nullptr };
    const std::vector<int>&      m_sequenceModuleNo; // integer array to keep track which module belong to which sequence

private:
    void InPowerUp(void);
    void InTask(void);
    void InAuto(void);
    UINT RunThread() override;

    struct stWarning
    {
        int  m_nWarningCode;  // warning code
        bool m_bWarningClear; // warning state, to clear or display
        bool m_bWarning;      // is there warning
    };

    std::deque<stWarning> m_deqWarning;
    std::set<int>         m_setWarn;

    bool m_bWarningClear{ true };
    bool m_bCountJam{ true };
    bool m_bJamCode{ false };
    bool m_bWarning{ false };
    bool m_bPowerUpComplete{ false };
    bool m_bJamflag{ false };
    bool m_bAutoRestart{ false };
    bool m_bPowerUpRestart{ false };
    int  m_nMode{ MODE_UNKNOWN };
    int  m_nPrevMode{ MODE_UNKNOWN };
    int  m_nState{ STATE_STOP };
    int  m_nJamCode{ 0 };
    int  m_nWarningCode{ 0 };

    int m_nModuleId;

    // Run Events
    int m_state{ RS_BEGIN };

    std::string m_strName;
    bool        m_bCloseLotComplete{ false };

    stMaintState m_stMaintState{};
    // Maintenance Parameters
    stMaintRunParameters m_stMaintParams{};

    //  all module no material status
    //  true = current module no material, false = current module got material
    static std::vector<bool> m_bNoMaterial;
    static int               m_iNoOfInstance; // keep track no of instance of this class

    //  to determine whether jam has been registered
    //  true = registered , false = unregistered
    bool m_bJamRegistered{ false };

    //  to determine whether this is a repeat jam
    //  true = repeat jam do not count this jam, false = new jam count this jam
    bool m_bJamOccuredBefore{ false };

    bool m_bModuleEnable{ false };

    // mutex to lock the warning message array
    CMutex m_WarningMutex;

    // flag to determine if maintenance operation is completed
    bool m_bMaintenanceOperationCompleted{ false };

    const stRuntimeMotorPosition* m_runtimeMotorPosition{ nullptr };
    const stRuntimeMotorSpeed*    m_runtimeMotorSpeed{ nullptr };

    const void* m_runtimePackageInfo{ nullptr };
    const void* m_runtimeRecipeInfo{ nullptr };
};
