/** @file   encoder.c
 *  @date   2014-12-04
 *  @author Frank Klee
 *  @brief  Drehencoder Library
 *
 *          Basisroutinen zum Abfragen eines Drehencoders mittels Polling.
 *          Quelle: https://www.mikrocontroller.net/articles/Drehgeber
 *
 *  @author Tom, DL7BJ
 *  @date   2023-03-23
 *  @brief  Formatierungen und Umbenennungen der Funktionen
 */
#include <avr/io.h>
#include <avr/interrupt.h>
#include "encoder.h"

int8_t DrehgeberPosition;
int8_t DrehgeberMax = 127;
int8_t DrehgeberMin = -127;

volatile int16_t iButtonPressedCounter = 0;
volatile int16_t iButtonDebounceCycles = 0;
volatile int16_t iButtonPressedLongCycles = 0;

volatile int8_t enc_delta;
static int8_t last;

typedef enum EButtonState
{
    ButtonState_Unpressed,
    ButtonState_Pressed,
    ButtonState_Hold,
    ButtonState_Released
}tEButtonState;

volatile tEButtonState buttonState = ButtonState_Unpressed;
volatile tEButtonPressedState buttonPressed = ButtonPressed_Unpressed;

void EncoderInit(void)
{
    int8_t new;
    new = 0;
    if(PHASE_A)
        new = 3;
    if(PHASE_B)
        new ^= 1;
    last = new;
    enc_delta = 0;
    iButtonDebounceCycles = BUTTON_DEBOUNCETIME_MS;
    iButtonPressedLongCycles = BUTTON_PRESSEDLONG_MS;
}
/** @fn     void EncoderPolling(void)
 *  @brief  EncoderPolling
 *
 *          Abfrage des Drehencoders und des Tasters
 *          Wird vom Timer 0 aufgerufen
 *
 *  @param  none
 *  @return none
*/
void EncoderPolling(void)
{
    int8_t new, diff;
    new = 0;
    if(PHASE_A) new = 3;
    if(PHASE_B) new ^= 1;
    diff = last - new;
    if(diff & 1) {
        last = new;
        enc_delta += (diff & 2) - 1;
    }
    switch(buttonState)
    {
        case ButtonState_Unpressed:
            if(BUTTONPRESSED) buttonState = ButtonState_Pressed;
        break;
        case ButtonState_Pressed:
            buttonState = ButtonState_Hold;
        break;
        case ButtonState_Hold:
            iButtonPressedCounter++;
            if(iButtonPressedCounter >= iButtonDebounceCycles && (! BUTTONPRESSED))
            {
                buttonState = ButtonState_Released;
                if(buttonPressed != ButtonPressed_Long) 
                    buttonPressed = ButtonPressed_Short;
            }
            if(iButtonPressedCounter >= iButtonPressedLongCycles)
            {
                buttonState = ButtonState_Released;
                buttonPressed = ButtonPressed_Long;
            }
        break;
        case ButtonState_Released:
            iButtonPressedCounter = 0;
            if(!BUTTONPRESSED)
                buttonState = ButtonState_Unpressed;
        break;
    }
}
#if defined (SingleStep)
int8_t EncodeRead(void)
{
    int8_t val;
    cli();
    val = enc_delta;
    enc_delta = 0;
    sei();
    return val;
}
#elif defined (TwoStep)
int8_t EncodeRead(void)
{
    int8_t val;
    cli();
    val = enc_delta;
    enc_delta = val & 1;
    sei();
    return val >> 1;
}
#elif defined (FourStep)
int8_t EncodeRead(void)
{
    int8_t val;
    cli();
    val = enc_delta;
    enc_delta = val & 3;
    sei();
    return val >> 2;
}
#endif

int8_t EncoderRead(char Ueberlauf)
{
    DrehgeberPosition += EncodeRead();
    if(DrehgeberPosition > DrehgeberMax)
    {
        if(Ueberlauf)
            DrehgeberPosition = DrehgeberPosition - DrehgeberMax + DrehgeberMin - 1;
        else
            DrehgeberPosition = DrehgeberMax;
    }
    if(DrehgeberPosition < DrehgeberMin)
    {
        if(Ueberlauf)
            DrehgeberPosition = DrehgeberPosition + DrehgeberMax - DrehgeberMin + 1;
        else
            DrehgeberPosition = DrehgeberMin;
    }
    return(DrehgeberPosition);
}
void EncoderWrite(int8_t EncoderPos)
{
    DrehgeberPosition = EncoderPos;
}

void EncoderMinMax(int8_t EncoderMin, int8_t EncoderMax)
{
    DrehgeberMin = EncoderMin;
    DrehgeberMax = EncoderMax;
    if(DrehgeberPosition > DrehgeberMax) DrehgeberPosition = DrehgeberMax;
    if(DrehgeberPosition < DrehgeberMin) DrehgeberPosition = DrehgeberMin;
}
tEButtonPressedState EncoderGetButtonState(void)
{
    tEButtonPressedState retVal = buttonPressed;
    buttonPressed = ButtonPressed_Unpressed;
    return retVal;
}
