#include "MyENcoder.h"

MyEncoder::MyEncoder(TIM_TypeDef *_tim_base, uint8_t _indexPin, void irq(void))
{
    tim_base = _tim_base;
    indexPin = _indexPin;
    attachInterrupt(digitalPinToInterrupt(indexPin), irq, RISING); // When Index triggered
    EncoderInit.SetCount(0);
}

#define ONE_PERIOD 65536
#define HALF_PERIOD 32768

int64_t MyEncoder::unwrapEncoder(uint16_t in)
{
    int32_t c32 = (int32_t)in - HALF_PERIOD;           // remove half period to determine (+/-) sign of the wrap
    int32_t dif = (c32 - previousEncoderCounterValue); // core concept: prev + (current - prev) = current

    // wrap difference from -HALF_PERIOD to HALF_PERIOD. modulo prevents differences after the wrap from having an incorrect result
    int32_t mod_dif = ((dif + HALF_PERIOD) % ONE_PERIOD) - HALF_PERIOD;
    if (dif < -HALF_PERIOD)
        mod_dif += ONE_PERIOD; // account for mod of negative number behavior in C

    int64_t unwrapped = previousEncoderCounterValue + mod_dif;
    previousEncoderCounterValue = unwrapped; // load previous value

    return unwrapped + HALF_PERIOD; // remove the shift we applied at the beginning, and return
}

void MyEncoder::indexPulse(void)
{
    if (pleaseZeroTheCounter)
    {
        tim_base->CNT = 0;
        indexPulseFired = 1;
        Pos.clear();
        TDelta.clear();
        pleaseZeroTheCounter = 0;
    }
}

uint8_t MyEncoder::indexHappened()
{
    if (indexPulseFired)
    {
        indexPulseFired = 0;
        previousEncoderCounterValue = 0;
        return 1;
    }
    return 0;
}

double MyEncoder::currentPos()
{
    curPos = unwrapEncoder(tim_base->CNT) * PosScaleRes;
    return curPos;
}

double MyEncoder::frequency(uint64_t time)
{

    double diffT = 0;
    double diffPos = 0;
    double frequency;
    TDelta.push(time); // Running average over the length of the circular buffer
    Pos.push(curPos);
    if (Pos.size() == RINGBUFFERLEN)
    {
        diffT = 1.0e-6 * (TDelta.last() - TDelta.first()); // Time is in microseconds
        diffPos = fabs(Pos.last() - Pos.first());
        frequency = diffPos / diffT;
        oldFrequency = frequency;
        return frequency; // Revolutions per second
    }
    else
        return oldFrequency;
}
uint8_t MyEncoder::getIndexState()
{
    return digitalRead(indexPin);
}

void MyEncoder::setScale(double scale)
{
    if (CurPosScale != scale && scale != 0)
    {
        CurPosScale = scale;
        PosScaleRes = 1.0 / double(scale);
    }
}

void MyEncoder::setLatch(uint8_t latchEnable)
{
    if (latchEnable && !oldLatchCEnable) // Should only happen first time IndexCEnable is set
    {
        pleaseZeroTheCounter = 1;
    }
    oldLatchCEnable = latchEnable;
}
