#include "buttontimer.hpp"

#include <time.h>
#ifndef CLOCK_MONOTIC
#   define CLOCK_MONOTIC CLOCK_REALTIME
#endif

ButtonTimer::ButtonTimer(uint32_t shortpress_min_ms, uint32_t longpress_ms)
{
    shortpressMinTime = shortpress_min_ms;
    longpressTime = longpress_ms;
    eventLock = false;
    
    ThreadStart();
}

ButtonTimer::~ButtonTimer()
{
    ThreadStop();

}

void ButtonTimer::RegisterPress(const uint8_t keycode)
{
    MutexLock();
    // Prevent trouble when calling this from within one of our event listeners
    if(eventLock) { MutexUnlock(); return; }

    pressRegistry[keycode] = now_ms();
    MutexUnlock();
}


void ButtonTimer::RegisterRelease(const uint8_t keycode)
{
    MutexLock();
    // Prevent trouble when calling this from within one of our event listeners
    if(eventLock) { MutexUnlock(); return; }
    
    if(pressRegistry.count(keycode))
    {
        // if it was a long press, the key code would already have been erased, so we
        // can safely fire the onShortPress event
        uint64_t now = now_ms();
        uint64_t then = pressRegistry[keycode];
        // remove from registry after release, if it was a long press, the event should have already been fired
        pressRegistry.erase(keycode); 
        
        if(now - then > shortpressMinTime)
        {
            eventLock = true; // lock out trouble
            onShortPress(keycode);
            eventLock = false; // risk of trouble gone
        }
    }

    MutexUnlock();
}

void ButtonTimer::CancelPress(const uint8_t keycode)
{
    // remove button id from map (but only if it is in the map already)
    MutexLock();
    // Prevent trouble when calling this from within one of our event listeners
    if(eventLock) { MutexUnlock(); return; }

    if(pressRegistry.count(keycode))
    {
        pressRegistry.erase(keycode);
    }
    MutexUnlock();
}

void ButtonTimer::ThreadLoop()
{
    uint64_t now = now_ms();
    std::list<uint8_t> btnList;
    boost::optional<bool> valid;
    MutexLock();
    // list through all the items 

    for( std::map<uint8_t,uint64_t>::iterator ii=pressRegistry.begin(); ii!=pressRegistry.end(); ++ii)
    {
        if(now - (ii->second) >= longpressTime) // if it is in overtime
        {
            // Schedule to process the thing
            btnList.push_back(ii->first);
        }
    }    

    // Process listed items
    for (std::list<uint8_t>::iterator it=btnList.begin(); it != btnList.end(); ++it)
    {
        pressRegistry.erase(*it);
        // If any validators are connected, they can retun false to indicate that this connection is not 
        // allowed
        eventLock = true; // lock out trouble
        
        valid = onValidatePress(*it);
        if(valid.get_value_or(true))
        {
            onLongPress(*it);
        }
        eventLock = false; // risk of trouble gone
    }
    
    MutexUnlock();
    usleep(50000);
}

int64_t ButtonTimer::now_ms(void)
{
    struct timespec now;
    clock_gettime(CLOCK_MONOTIC, &now);

    return ((int64_t)now.tv_sec)*1000LL + (now.tv_nsec/1000000);
}
