#include "GwButtonTask.h"
#include "GwHardware.h"
#include "GwApi.h"
#include "GwLedTask.h"

class FactoryResetRequest: public GwMessage{
    private:
        GwApi *api;
    public:
        FactoryResetRequest(GwApi *api):GwMessage(F("reset button")){
            this->api=api;
        }
        virtual ~FactoryResetRequest(){}
    protected:    
        virtual void processImpl(){
            api->getLogger()->logDebug(GwLog::LOG,"reset request processing");
            api->getConfig()->reset();
            xTaskCreate([](void *p){
            delay(500);
            ESP.restart();
            vTaskDelete(NULL);
            },"reset",1000,NULL,0,NULL);
        };
};
void handleButtons(GwApi *api){
    GwLog *logger=api->getLogger();
    GwApi::TaskInterfaces *interfaces=api->taskInterfaces();
    IButtonTask state;
    if (!interfaces->set(state)){
        LOG_DEBUG(GwLog::ERROR,"unable to set button state");
    } 
    #ifndef GWBUTTON_PIN
        LOG_DEBUG(GwLog::LOG,"no button pin defined, do not watch");
        vTaskDelete(NULL);
        return;
    #else
    #ifndef GWBUTTON_ACTIVE
    int activeState=0; 
    #else
    int activeState=GWBUTTON_ACTIVE;
    #endif
    #ifdef GWBUTTON_PULLUPDOWN
    bool pullUpDown=true;
    #else
    bool pullUpDown=false;
    #endif
    uint8_t mode=INPUT;
    if (pullUpDown){
        mode=activeState?INPUT_PULLDOWN:INPUT_PULLUP;
    }
    pinMode(GWBUTTON_PIN,mode);
    unsigned long lastPressed=0;
    unsigned long firstPressed=0;
    unsigned long lastReport=0;
    const unsigned long OFF_TIME=20;
    const unsigned long REPORT_TIME=1000;
    const unsigned long PRESS_5_TIME=5000;
    const unsigned long PRESS_10_TIME=10000;
    const unsigned long PRESS_RESET_TIME=12000;
    LOG_DEBUG(GwLog::LOG,"button task started");
    while(true){
        delay(10);
        int current=digitalRead(GWBUTTON_PIN);
        unsigned long now=millis();
        IButtonTask::ButtonState lastState=state.state;
        if (current != activeState){
            if (lastPressed != 0 && (lastPressed+OFF_TIME) < now){
                lastPressed=0; //finally off
                firstPressed=0;
                state.state=IButtonTask::OFF;
                LOG_DEBUG(GwLog::LOG,"Button press stopped");
            }
            if (state.state != lastState){
                interfaces->set(state);
            }
            continue;
        }
        lastPressed=now;
        if (firstPressed == 0) {
            firstPressed=now;
            LOG_DEBUG(GwLog::LOG,"Button press started");
            state.pressCount++;
            state.state=IButtonTask::PRESSED;
            interfaces->set(state);
            lastReport=now;
            continue;
        }
        if (lastReport != 0 && (lastReport + REPORT_TIME) < now ){
            LOG_DEBUG(GwLog::LOG,"Button active for %ld",(now-firstPressed));
            lastReport=now;
        }
        if (now > (firstPressed+PRESS_5_TIME)){
            state.state=IButtonTask::PRESSED_5;
        }
        if (now > (firstPressed+PRESS_10_TIME)){
            state.state=IButtonTask::PRESSED_10;
        }
        if (lastState != state.state){
            interfaces->set(state);
        }
        if (now > (firstPressed+PRESS_RESET_TIME)){
            LOG_DEBUG(GwLog::ERROR,"Factory reset by button");
            GwMessage *r=new FactoryResetRequest(api);
            api->getQueue()->sendAndForget(r);
            r->unref();
            firstPressed=0;
            lastPressed=0;
        }
    }
    vTaskDelete(NULL);
    #endif
}

void initButtons(GwApi *api){
    #ifndef GWBUTTON_PIN
    api->getLogger()->logDebug(GwLog::LOG,"no buttons defined, no button task");
    return;
    #endif
    const String taskname("buttonTask");
    api->addUserTask(handleButtons,taskname);
    api->taskInterfaces()->claim<IButtonTask>(taskname);
}