#include "UpDownInterruptBase.h"
#include "configuration.h"

UpDownInterruptBase::UpDownInterruptBase(const char *name) : concurrency::OSThread(name)
{
    this->_originName = name;
}

void UpDownInterruptBase::init(uint8_t pinDown, uint8_t pinUp, uint8_t pinPress, input_broker_event eventDown,
                               input_broker_event eventUp, input_broker_event eventPressed, input_broker_event eventPressedLong,
                               input_broker_event eventUpLong, input_broker_event eventDownLong, void (*onIntDown)(),
                               void (*onIntUp)(), void (*onIntPress)(), unsigned long updownDebounceMs)
{
    this->_pinDown = pinDown;
    this->_pinUp = pinUp;
    this->_pinPress = pinPress;
    this->_eventDown = eventDown;
    this->_eventUp = eventUp;
    this->_eventPressed = eventPressed;
    this->_eventPressedLong = eventPressedLong;
    this->_eventUpLong = eventUpLong;
    this->_eventDownLong = eventDownLong;

    // Store debounce configuration passed by caller
    this->updownDebounceMs = updownDebounceMs;
    bool isRAK = false;
#ifdef RAK_4631
    isRAK = true;
#endif

    if (!isRAK || pinPress != 0) {
        pinMode(pinPress, INPUT_PULLUP);
        attachInterrupt(pinPress, onIntPress, FALLING);
    }
    if (!isRAK || this->_pinDown != 0) {
        pinMode(this->_pinDown, INPUT_PULLUP);
        attachInterrupt(this->_pinDown, onIntDown, FALLING);
    }
    if (!isRAK || this->_pinUp != 0) {
        pinMode(this->_pinUp, INPUT_PULLUP);
        attachInterrupt(this->_pinUp, onIntUp, FALLING);
    }

    LOG_DEBUG("Up/down/press GPIO initialized (%d, %d, %d)", this->_pinUp, this->_pinDown, pinPress);

    this->setInterval(20);
}

int32_t UpDownInterruptBase::runOnce()
{
    InputEvent e = {};
    e.inputEvent = INPUT_BROKER_NONE;
    unsigned long now = millis();

    // Read all button states once at the beginning
    bool pressButtonPressed = !digitalRead(_pinPress);
    bool upButtonPressed = !digitalRead(_pinUp);
    bool downButtonPressed = !digitalRead(_pinDown);

    // Handle initial button press detection - only if not already detected
    if (this->action == UPDOWN_ACTION_PRESSED && pressButtonPressed && !pressDetected) {
        pressDetected = true;
        pressStartTime = now;
    } else if (this->action == UPDOWN_ACTION_UP && upButtonPressed && !upDetected) {
        upDetected = true;
        upStartTime = now;
    } else if (this->action == UPDOWN_ACTION_DOWN && downButtonPressed && !downDetected) {
        downDetected = true;
        downStartTime = now;
    }

    // Handle long press detection for press button
    if (pressDetected && pressStartTime > 0) {
        uint32_t pressDuration = now - pressStartTime;

        if (!pressButtonPressed) {
            // Button released
            if (pressDuration < LONG_PRESS_DURATION && now - lastPressKeyTime >= pressDebounceMs) {
                lastPressKeyTime = now;
                e.inputEvent = this->_eventPressed;
            }
            // Reset state
            pressDetected = false;
            pressStartTime = 0;
            lastPressLongEventTime = 0;
        } else if (pressDuration >= LONG_PRESS_DURATION && lastPressLongEventTime == 0) {
            // First long press event only - avoid repeated events causing lag
            e.inputEvent = this->_eventPressedLong;
            lastPressLongEventTime = now;
        }
    }

    // Handle long press detection for up button
    if (upDetected && upStartTime > 0) {
        uint32_t upDuration = now - upStartTime;

        if (!upButtonPressed) {
            // Button released
            if (upDuration < LONG_PRESS_DURATION && now - lastUpKeyTime >= updownDebounceMs) {
                lastUpKeyTime = now;
                e.inputEvent = this->_eventUp;
            }
            // Reset state
            upDetected = false;
            upStartTime = 0;
            lastUpLongEventTime = 0;
        } else if (upDuration >= LONG_PRESS_DURATION) {
            // Auto-repeat long press events
            if (lastUpLongEventTime == 0 || (now - lastUpLongEventTime) >= LONG_PRESS_REPEAT_INTERVAL) {
                e.inputEvent = this->_eventUpLong;
                lastUpLongEventTime = now;
            }
        }
    }

    // Handle long press detection for down button
    if (downDetected && downStartTime > 0) {
        uint32_t downDuration = now - downStartTime;

        if (!downButtonPressed) {
            // Button released
            if (downDuration < LONG_PRESS_DURATION && now - lastDownKeyTime >= updownDebounceMs) {
                lastDownKeyTime = now;
                e.inputEvent = this->_eventDown;
            }
            // Reset state
            downDetected = false;
            downStartTime = 0;
            lastDownLongEventTime = 0;
        } else if (downDuration >= LONG_PRESS_DURATION) {
            // Auto-repeat long press events
            if (lastDownLongEventTime == 0 || (now - lastDownLongEventTime) >= LONG_PRESS_REPEAT_INTERVAL) {
                e.inputEvent = this->_eventDownLong;
                lastDownLongEventTime = now;
            }
        }
    }

    if (e.inputEvent != INPUT_BROKER_NONE) {
        e.source = this->_originName;
        e.kbchar = INPUT_BROKER_NONE;
        this->notifyObservers(&e);
    }

    if (!pressDetected && !upDetected && !downDetected) {
        this->action = UPDOWN_ACTION_NONE;
    }

    return 20; // This will control how the input frequency
}

void UpDownInterruptBase::intPressHandler()
{
    this->action = UPDOWN_ACTION_PRESSED;
}

void UpDownInterruptBase::intDownHandler()
{
    this->action = UPDOWN_ACTION_DOWN;
}

void UpDownInterruptBase::intUpHandler()
{
    this->action = UPDOWN_ACTION_UP;
}
