#pragma once

#include <Automaton.h>
#include <AccelStepper.h>

class Atm_AccelStepper: public Machine {

 public:
  enum { DISABLE, ENABLED, RUNNING, STOP, HOMING_LOW, HOMING_HIGH, LIMIT_LOW, LIMIT_HIGH }; // STATES
  enum { EVT_DISABLE, EVT_ENABLE, EVT_ENABLED_TIMEOUT, EVT_MOVE, EVT_STOP,
          EVT_EMERGENCY_STOP, EVT_ON_LIMIT_LOW, EVT_ON_LIMIT_HIGH, EVT_ON_TARGET,
          EVT_HOMING_LOW, EVT_HOMING_HIGH, ELSE }; // EVENTS
  Atm_AccelStepper( void ) : Machine() {};
  Atm_AccelStepper& begin( int step_pin, int dir_pin );
  Atm_AccelStepper& trace( Stream & stream );
  Atm_AccelStepper& trigger( int event );
  int state( void );
  Atm_AccelStepper& onChangeposition( Machine& machine, int event = 0 );
  Atm_AccelStepper& onChangeposition( atm_cb_push_t callback, int idx = 0 );
  Atm_AccelStepper& onChangestate( Machine& machine, int event = 0 );
  Atm_AccelStepper& onChangestate( atm_cb_push_t callback, int idx = 0 );
  Atm_AccelStepper& onOnlimithigh( Machine& machine, int event = 0 );
  Atm_AccelStepper& onOnlimithigh( atm_cb_push_t callback, int idx = 0 );
  Atm_AccelStepper& onOnlimitlow( Machine& machine, int event = 0 );
  Atm_AccelStepper& onOnlimitlow( atm_cb_push_t callback, int idx = 0 );
  Atm_AccelStepper& onOntarget( Machine& machine, int event = 0 );
  Atm_AccelStepper& onOntarget( atm_cb_push_t callback, int idx = 0 );
  Atm_AccelStepper& onStop( Machine& machine, int event = 0 );
  Atm_AccelStepper& onStop( atm_cb_push_t callback, int idx = 0 );
  Atm_AccelStepper& onOnhominglow( Machine& machine, int event = 0 );
  Atm_AccelStepper& onOnhominglow( atm_cb_push_t callback, int idx = 0 );
  Atm_AccelStepper& onOnhominghigh( Machine& machine, int event = 0 );
  Atm_AccelStepper& onOnhominghigh( atm_cb_push_t callback, int idx = 0 );
  Atm_AccelStepper& disable( void );
  Atm_AccelStepper& enable( void );
  //Atm_AccelStepper& move( void );
  Atm_AccelStepper& stop( void );
  Atm_AccelStepper& emergency_stop( void );
  Atm_AccelStepper& on_limit_low( void );
  Atm_AccelStepper& on_limit_high( void );
  Atm_AccelStepper& on_target( void );

  AccelStepper *stepper;

  long int homing_speed = 100;
  long int max_speed = 5000;
  long int acceleration = 1000;
  Atm_AccelStepper& setMaxSpeed( long int maxSpeed = 5000);
  Atm_AccelStepper& setHomingSpeed( long int homingSpeed = 400);
  Atm_AccelStepper& setAcceleration( long int acc = 1000);

  Atm_AccelStepper& move(long int stepRel );
  Atm_AccelStepper& moveTo(long int stepAbs );
  Atm_AccelStepper& rotate(long int speed );
  Atm_AccelStepper& homing( bool direction );
  int runMode = 0; // 0 uses run() for positioning, 1 uses runSpeed() for constant speed
  Atm_AccelStepper& position_refresh( long int refresh_ms = 1000);

  // Atm_AccelStepper& rotationReversed(bool reversed);

  Atm_AccelStepper& setEnablePin( int enablePin );
  Atm_AccelStepper& pinReversed( bool directionInvert=false,  bool stepInvert=false, bool enableInvert=false );
  bool enabled ;


  Atm_AccelStepper& limitLow_set(int mode = 0, int pin = -1, int reversed=0);
  Atm_AccelStepper& limitLow_isHard(bool hardlimit = 1);
  Atm_AccelStepper& limitLow_setThresholds (int threshold_low=510, int threshold_high = 1024);
  Atm_AccelStepper& limitHigh_set(int mode = 0, int pin = -1, int reversed=0);
  Atm_AccelStepper& limitHigh_isHard(bool hardlimit = 1);
  Atm_AccelStepper& limitHigh_setThresholds (int threshold_low=510, int threshold_high = 1024);
  bool limitLow_State;
  bool limitHigh_State;



 private:
  enum { ENT_DISABLED, ENT_ENABLED,ENT_RUNNING, LP_RUNNING, ENT_STOP, LP_STOP, ENT_HOMING_LOW, LP_HOMING_LOW, EXT_HOMING_LOW, ENT_HOMING_HIGH, LP_HOMING_HIGH, EXT_HOMING_HIGH, ENT_LIMIT_LOW, ENT_LIMIT_HIGH }; // ACTIONS
  enum { ON_CHANGEPOSITION, ON_CHANGESTATE, ON_ONLIMITHIGH, ON_ONLIMITLOW, ON_ONTARGET, ON_STOP, CONN_MAX }; // CONNECTORS
  atm_connector connectors[CONN_MAX];
  int event( int id );
  void action( int id );

  void stepper_update(void);

  long int _currentStep = 0;
  long int _targetStep = 0;
  long int _maxStep ;
  atm_timer_millis position_timer ;
  int POSITION_SEND_TIMER = 50 ;

  // bool _rotationReversed = 0 ;

  int _enablePin = -1;
  bool _enableReversed = 0 ;
  atm_timer_millis idle_timer ;
  int IDLE_TIMEOUT_DURATION = 500000 ;

  int _limitLow_Pin;
  int _limitLow_Mode = 0; //0 no limit, 1 digital, 2 analog with thresholds
  bool _limitLow_Reversed ; //invert logic of limit switches
  int _limitLow_Thresholds[2] ; //analog value  range for two analog limits
  bool _limitLow_Hard = 1;
  int _limitHigh_Pin;
  int _limitHigh_Mode; //0 no limit, 1 digital, 2 analog with thresholds
  bool _limitHigh_Reversed ; //invert logic of limit switches
  int _limitHigh_Thresholds[2] ; //analog value  range for two analog limits
  bool _limitHigh_Hard = 1 ;
  int _isHoming = 0 ;
  void updateLimitSwitch();

};

/*
Automaton::ATML::begin - Automaton Markup Language

<?xml version="1.0" encoding="UTF-8"?>
<machines>
  <machine name="Atm_AccelStepper">
    <states>
      <DISABLED index="0" on_enter="ENT_DISABLED">
        <EVT_ENABLE>ENABLED</EVT_ENABLE>
      </DISABLED>
      <ENABLED index="1" on_enter="ENT_ENABLED">
        <EVT_DISABLE>DISABLED</EVT_DISABLE>
        <EVT_ENABLED_TIMEOUT>DISABLED</EVT_ENABLED_TIMEOUT>
        <EVT_STOP>STOP</EVT_STOP>
        <EVT_EMERGENCY_STOP>STOP</EVT_EMERGENCY_STOP>
      </ENABLED>
      <RUNNING index="2" on_loop="LP_RUNNING">
        <EVT_DISABLE>DISABLED</EVT_DISABLE>
        <EVT_STOP>STOP</EVT_STOP>
        <EVT_EMERGENCY_STOP>STOP</EVT_EMERGENCY_STOP>
        <EVT_ON_LIMIT_LOW>LIMIT_LOW</EVT_ON_LIMIT_LOW>
        <EVT_ON_LIMIT_HIGH>LIMIT_HIGH</EVT_ON_LIMIT_HIGH>
        <EVT_ON_TARGET>ENABLED</EVT_ON_TARGET>
      </RUNNING>
      <STOP index="3">
        <EVT_DISABLE>DISABLED</EVT_DISABLE>
        <EVT_STOP>STOP</EVT_STOP>
        <EVT_EMERGENCY_STOP>STOP</EVT_EMERGENCY_STOP>
      </STOP>
      <HOMING_LOW index="4" on_enter="ENT_HOMING_LOW" on_exit="EXT_HOMING_LOW">
        <EVT_DISABLE>DISABLED</EVT_DISABLE>
        <EVT_STOP>STOP</EVT_STOP>
        <EVT_EMERGENCY_STOP>STOP</EVT_EMERGENCY_STOP>
        <EVT_ON_LIMIT_LOW>ENABLED</EVT_ON_LIMIT_LOW>
      </HOMING_LOW>
      <HOMING_HIGH index="5" on_enter="ENT_HOMING_HIGH" on_exit="EXT_HOMING_HIGH">
        <EVT_DISABLE>DISABLED</EVT_DISABLE>
        <EVT_STOP>STOP</EVT_STOP>
        <EVT_EMERGENCY_STOP>STOP</EVT_EMERGENCY_STOP>
        <EVT_ON_LIMIT_HIGH>ENABLED</EVT_ON_LIMIT_HIGH>
      </HOMING_HIGH>
      <LIMIT_LOW index="6" on_enter="ENT_LIMIT_LOW">
        <EVT_STOP>STOP</EVT_STOP>
        <EVT_EMERGENCY_STOP>STOP</EVT_EMERGENCY_STOP>
        <EVT_ON_LIMIT_LOW>LIMIT_LOW</EVT_ON_LIMIT_LOW>
      </LIMIT_LOW>
      <LIMIT_HIGH index="7" on_enter="ENT_LIMIT_HIGH">
        <EVT_STOP>STOP</EVT_STOP>
        <EVT_EMERGENCY_STOP>STOP</EVT_EMERGENCY_STOP>
        <EVT_ON_LIMIT_HIGH>LIMIT_HIGH</EVT_ON_LIMIT_HIGH>
      </LIMIT_HIGH>
    </states>
    <events>
      <EVT_DISABLE index="0" access="MIXED"/>
      <EVT_ENABLE index="1" access="MIXED"/>
      <EVT_ENABLED_TIMEOUT index="2" access="PRIVATE"/>
      <EVT_STOP index="3" access="MIXED"/>
      <EVT_EMERGENCY_STOP index="4" access="MIXED"/>
      <EVT_ON_LIMIT_LOW index="5" access="MIXED"/>
      <EVT_ON_LIMIT_HIGH index="6" access="MIXED"/>
      <EVT_ON_TARGET index="7" access="MIXED"/>
    </events>
    <connectors>
      <CHANGEPOSITION autostore="0" broadcast="0" dir="PUSH" slots="1"/>
      <CHANGESTATE autostore="0" broadcast="0" dir="PUSH" slots="1"/>
      <ONLIMITHIGH autostore="0" broadcast="0" dir="PUSH" slots="1"/>
      <ONLIMITLOW autostore="0" broadcast="0" dir="PUSH" slots="1"/>
      <ONTARGET autostore="0" broadcast="0" dir="PUSH" slots="1"/>
      <STOP autostore="0" broadcast="0" dir="PUSH" slots="1"/>
    </connectors>
    <methods>
    </methods>
  </machine>
</machines>

Automaton::ATML::end
*/
