#pragma once
//
//    FILE: PulseDivider.h
//  AUTHOR: Rob Tillaart
// VERSION: 0.1.3
//    DATE: 2023-09-13
// PURPOSE: Arduino library to divide a pulse stream with a float factor.
//     URL: https://github.com/RobTillaart/PulseDivider


#include "Arduino.h"

#define PULSEDIVIDER_LIB_VERSION        (F("0.1.3"))


class PulseDivider
{
public:
  //  CONSTRUCTOR
  PulseDivider(uint8_t inPin, uint8_t outPin, uint16_t inCount, uint16_t outCount,
                uint32_t duration = 1, uint8_t edge = RISING, bool invert = false)
  {
    setInvert(invert);  //  must be done before setOutPin() !
    setInPin(inPin);
    setOutPin(outPin);
    setRatio(inCount, outCount);
    setDuration(duration);
    setEdge(edge);
    _prevState = _read();
    stop();
  }


///////////////////////////////////////////
//
//  GETTERS && SETTERS
//
  void  setInPin(uint8_t inPin)
  {
    _inPin = inPin;
    pinMode(_inPin, INPUT);
  }

  uint8_t getInPin()
  {
    return _inPin;
  }

  void  setOutPin(uint8_t outPin)
  {
    _outPin = outPin;
    pinMode(_outPin, OUTPUT);
    _write(_invert ? HIGH : LOW);
  }

  uint8_t getOutPin()
  {
    return _outPin;
  }

  void setRatio(uint16_t inCount, uint16_t outCount = 1)
  {
    _inCount   = inCount;
    _outCount  = outCount;
    _counter   = inCount / 2;  //  reasonable start position.
  }

  float getRatio()
  {
    return float(_inCount) / _outCount;
  }

  void setDuration(uint32_t duration)
  {
    _duration = duration;
  }

  uint32_t getDuration()
  {
    return _duration;
  }

  void setEdge(uint8_t edge)
  {
    if ((edge != FALLING) && (edge != RISING)) return;
    _edge = edge;
  }

  uint8_t getEdge()
  {
    return _edge;
  }

  void setInvert(bool invert)
  {
    _invert = invert;
    _write(_invert ? HIGH : LOW);
  }

  bool getInvert()
  {
    return _invert;
  }

  //  debug
  uint16_t getCounter()
  {
    return _counter;
  }

///////////////////////////////////////////
//
//  CONTROL
//
  void start()
  {
    _prevState = _read();
    _counter   = _inCount / 2;  //  reasonable start position.
    _running   = true;
  }

  void stop()
  {
    _running  = false;
    _write(_invert ? HIGH : LOW);
  }

  bool isRunning()
  {
    return _running;
  }


///////////////////////////////////////////
//
//  MAIN WORKER
//
  void check()
  {
    if (_running == false) return;

    uint8_t value = _read();
    if (_prevState != value)
    {
       if (((_edge == RISING) and (value == HIGH)) or
           ((_edge == FALLING) and (value == LOW)))
       {
           doPulse();
       }
    }
    _prevState = value;

    //  reset the output?
    if (_pulseStarted and (micros() - _start >= _duration))
    {
      _write(_invert ? HIGH : LOW);
      _pulseStarted = false;
    }
  }

  inline void doPulse()
  {
    _counter += _outCount;
    if (_counter >= _inCount)
    {
      _counter -= _inCount;
      _start = micros();
      _write(_invert ? LOW : HIGH);
      _pulseStarted = true;
    }
  }


private:

  //  default reference
  inline void _write(uint8_t value)
  {
    digitalWrite(_outPin, value);
  }

  inline uint8_t _read()
  {
    return digitalRead(_inPin);
  }

  uint8_t  _inPin;
  uint8_t  _outPin;
  uint16_t _inCount   = 1;
  uint16_t _outCount  = 1;
  uint16_t _counter   = 0;

  uint32_t _duration  = 1;
  uint8_t  _edge      = RISING;
  bool     _invert    = false;

  bool     _running   = false;
  uint8_t  _prevState = LOW;
  uint32_t _start     = 0;
  bool     _pulseStarted = false;
};


//  -- END OF FILE --

