#ifndef OMRON_VFD_H
#define OMRON_VFD_H

#include <Component.h>
#include <SRegister.h>
#include <xtimer.h>
#include <types.h>
#include <ModbusValue.h>
#include <xstatistics.h>
#include <macros.h>
#include <Vector.h>

#include "./config.h"
#include "./OmronE5.h"
#include "./ModbusBridge.h"

class PHApp;
class MotorLoad;
class Pos3Analog;
class POT;

#define E_VFD_MB_QUEUE_STATUS 1
#define E_VFD_MB_QUEUE_DIR 2
#define E_VFD_MB_QUEUE_AMPS 3
#define E_VFD_MB_QUEUE_CMD 4
#define E_VFD_MB_QUEUE_LENGTH 1

// actual PID, holds only values and handy functions
class OmronVFDState
{
public:
  OmronVFDState() : lastUpdated(millis()),
                    lastWritten(millis()),
                    queue({E_VFD_MB_QUEUE_STATUS})
  // E_VFD_MB_QUEUE_DIR,
  // E_VFD_MB_QUEUE_AMPS})
  {
  }

  struct type_errorMX2 // error structure
  {
    uint16_t code;   // reason
    uint16_t status; // Inverter status on shutdown
    uint16_t noUse;  // Not used
    uint16_t fr;     // IF frequency during shutdown
    uint16_t cur;    // IF current on shutdown
    uint16_t vol;    // IF voltage when disconnected
    uint32_t time1;  // Total running time in STROKE mode when disconnected
    uint32_t time2;  // Total operating time of the inverter with the power on at the time of shutdown
  };
  union union_errorFC // Omron Error Translation
  {
    type_errorMX2 MX2;
    uint16_t inputBuf[10];
  };

  int8_t err;          // last error
  uint16_t numErr;     // number of errors
  uint8_t nbComErrors; // The number of communication errors when exceeding FC_NUM_READ inverter lock 485 control
  uint16_t FC;         // Inverter target frequency in 0.01 hertz
  uint16_t power;      // Read: Current inverter power in 100 watt units (3 is 300 watts)
  uint16_t current;    // Read: Current inverter current in 0.01 Amp units
  int8_t max_current;  // Read: Maximum current inverter in 0.01 Amp units, sample storage = 20 Frames

  int16_t state;  // Read: State of the inverter register MX2_STATE
  int16_t status; // Read: Status of the inverter register MX2_STATUS
  uint8_t direction; // Read: Direction of the inverter register MX2_DIRECTION

  millis_t startTS;    // compressor start time
  union_errorFC error; // Structure for decoding the inverter error

  millis_t lastUpdated;
  millis_t lastWritten;
  ShiftRegister<uchar, E_VFD_MB_QUEUE_LENGTH> queue;

  void print()
  {
    Log.verboseln("FC:%d | State:%d | Status:%d", FC, state, status);
  }
};

// Addon to deal with multiple Omron PID controllers
class OmronVFD : public Component, public ModbusValue<int[]>
{
public:
  typedef enum
  {
    E_VFD_MODE_NONE = 0,
    E_VFD_MODE_DIRECT = 1,
    E_VFD_MODE_MODBUS = 2,
    E_VFD_MODE_UNKNOWN = 3
  } E_VFD_MODE;

  typedef enum
  {
    E_VFD_DIR_NONE = 0,
    E_VFD_DIR_FORWARD = 1,
    E_VFD_DIR_REVERSE = 2,
  } E_VFD_DIR;

  typedef enum
  {
    E_VFD_RUN_MODE_NONE = 0,
    E_VFD_RUN_MODE_RUN = 1,
    E_VFD_RUN_MODE_STOP = 2,
    E_VFD_RUN_MODE_STOP_RETRACT = 3,
  } E_VFD_RUN_MODE;

  typedef enum
  {
    E_VFD_RETRACT_STATE_NONE = 0,
    E_VFD_RETRACT_STATE_BRAKING = 1,
    E_VFD_RETRACT_STATE_STOPPED = 2,
    E_VFD_RETRACT_STATE_REVERSING = 3,
    E_VFD_RETRACT_STATE_BRAKE_REVERSING = 4,
    E_VFD_RETRACT_STATE_RETRACTED = 5,
  } E_VFD_RETRACT_STATE;

  typedef enum
  {
    E_VFD_STATE_ACCELERATING = 4,
    E_VFD_STATE_DECELERATING = 2,
    E_VFD_STATE_RUNNING = 3,
    E_VFD_STATE_STOPPED = 1,
    E_VFD_STATE_ERROR = 8
  } E_VFD_STATE;

  OmronVFD(
      ModbusBridge *_bridge,
      short _slaveId,
      Component *_owner) : ModbusValue<int[]>(MB_W_VFD_RUN, MB_FC::MB_FC_READ_REGISTERS),
                           Component("VFD", COMPONENT_KEY_VFD, Component::COMPONENT_DEFAULT, _owner),
                           modbus(_bridge),
                           mode(NULL),
                           dir(NULL),
                           speed(NULL),
                           slaveAddress(_slaveId)
  {
    // setFlag(OBJECT_RUN_FLAGS::E_OF_DEBUG);
    SBI(nFlags, OBJECT_NET_CAPS::E_NCAPS_MODBUS);
    readStateTS = bootTS = last = mbTS = millis();
    direction = lastError = stopTS = runTS = lastWriteValue = lastWriteAddress = 0;
    runMode = E_VFD_RUN_MODE::E_VFD_RUN_MODE_NONE;
    mbInterval = readInterval = OMRON_MX2_STATE_INTERVAL;
    direction = E_VFD_DIR::E_VFD_DIR_FORWARD;
    setNumberAddresses(4);
    setFunctionCode(MB_FC::MB_FC_READ_REGISTERS);
    setRegisterMode(MB_REGISTER_MODE::E_MB_REGISTER_MODE_READ);
  }

  //////////////////////////////////////////
  // Component implementation
  short loop();
  short setup();
  short debug();
  short info();
  short onRegisterMethods(Bridge *bridge);

  ///////////////////////////////////////////
  // Modbus
  short readSingle_16(int addr, int prio = 0);
  short read_16(int addr, int num, int prio = 0);
  uint16_t read_coil_single(uint16_t addr);
  uint16_t write_Single(uint16_t cmd, unsigned int data);
  uint16_t write_Bit(uint16_t addr, int on);
  // Modbus callbacks
  short onResponse(short error);
  short onError(short error);
  short onRawResponse(short size, uint8_t rxBuffer[]);
  bool skipRead(int slave, int fn, int addr, int num, int prio);
  ///////////////////////////////////////////
  // VFD control
  uint16_t setTargetFreq(uint16_t freq);
  uint16_t stop();
  uint16_t run();
  uint16_t reverse();
  uint16_t forward();
  short onStart();
  short onStop();

  ///////////////////////////////////////////
  // Basics

  uint16_t configure();
  uint16_t updateState();
  millis_t last;
  millis_t readStateTS;
  millis_t mbTS;
  millis_t debugTS;
  millis_t bootTS;

  ///////////////////////////////////////////
  // Options

  POT *speed;
  Pos3Analog *mode;
  Pos3Analog *dir;
  MotorLoad *motorLoad;
  Statistic currentStats;
  PHApp *owner;
  OmronVFDState *getVFDState()
  {
    return &states[0];
  }

  short directDirection();
  E_VFD_DIR direction;

private:
  ModbusBridge *modbus;
  short slaveAddress;
  OmronVFDState states[1];

  short ping();
  void updateTCP();
  short fromTCP();
  short modbusLoop();

  short lastError;
  millis_t readInterval;
  millis_t mbInterval;
  millis_t stopTS;
  millis_t runTS;
  millis_t reverseStartTS;

  E_VFD_RUN_MODE runMode;
  E_VFD_RETRACT_STATE retractState;

  
  uint16_t lastWriteAddress;
  uint16_t lastWriteValue;
};

#endif