#include "gpio_evaluateSwitch.hpp"

static const char *TAG = "evaluateSwitch";


gpio_evaluatedSwitch::gpio_evaluatedSwitch( //minimal (use default values)
        gpio_num_t gpio_num_declare
        ){
    gpio_num = gpio_num_declare;
    pullup = true;
    inverted = false;

    init();
};



gpio_evaluatedSwitch::gpio_evaluatedSwitch( //optional parameters given
        gpio_num_t gpio_num_declare,
        bool pullup_declare,
        bool inverted_declare
        ){
    gpio_num = gpio_num_declare;
    pullup = pullup_declare;
    inverted = inverted_declare;

    init();
};



void gpio_evaluatedSwitch::init(){
    ESP_LOGI(TAG, "initializing gpio pin %d", (int)gpio_num);

    //define gpio pin as input
    gpio_pad_select_gpio(gpio_num);
    gpio_set_direction(gpio_num, GPIO_MODE_INPUT);

    if (pullup == true){ //enable pullup if desired (default)
        gpio_pad_select_gpio(gpio_num);
        gpio_set_pull_mode(gpio_num, GPIO_PULLUP_ONLY);
    }else{
        gpio_set_pull_mode(gpio_num, GPIO_FLOATING);
        gpio_pad_select_gpio(gpio_num);
    }
    //TODO add pulldown option
    //gpio_set_pull_mode(gpio_num, GPIO_PULLDOWN_ONLY);
};


void gpio_evaluatedSwitch::handle(){  //Statemachine for debouncing and edge detection
    if (inverted == true){
        //=========================================================
        //=========== Statemachine for inverted switch ============
        //=================== (switch to VCC) =====================
        //=========================================================
        switch (p_state){
            default:
                p_state = switchState::FALSE;
                break;

            case switchState::FALSE:  //input confirmed high (released)
                fallingEdge = false; //reset edge event
                if (gpio_get_level(gpio_num) == 1){ //pin high (on)
                    p_state = switchState::HIGH;
                    timestampHigh = esp_log_timestamp(); //save timestamp switched from low to high
                } else {
                    msReleased = esp_log_timestamp() - timestampLow; //update duration released
                }
                break;

            case switchState::HIGH: //input recently switched to high (pressed)
                if (gpio_get_level(gpio_num) == 1){ //pin still high (on)
                    if (esp_log_timestamp() - timestampHigh > minOnMs){ //pin in same state long enough
                        p_state = switchState::TRUE;
                        state = true;
                        risingEdge = true;
                        msReleased = timestampHigh - timestampLow; //calculate duration the button was released 
                    }
                }else{
                    p_state = switchState::FALSE;
                }
                break;

            case switchState::TRUE:  //input confirmed high (pressed)
                risingEdge = false; //reset edge event
                if (gpio_get_level(gpio_num) == 0){ //pin low (off)
                    timestampLow = esp_log_timestamp();
                    p_state = switchState::LOW;
                } else {
                    msPressed = esp_log_timestamp() - timestampHigh; //update duration pressed
                }

                break;

            case switchState::LOW: //input recently switched to low (released)
                if (gpio_get_level(gpio_num) == 0){ //pin still low (off)
                    if (esp_log_timestamp() - timestampLow > minOffMs){ //pin in same state long enough
                        p_state = switchState::FALSE;
                        msPressed = timestampLow - timestampHigh; //calculate duration the button was pressed
                        state=false;
                        fallingEdge=true;
                    }
                }else{
                    p_state = switchState::TRUE;
                }
                break;
        }

    }else{
        //=========================================================
        //========= Statemachine for not inverted switch ==========
        //=================== (switch to GND) =====================
        //=========================================================
        switch (p_state){
            default:
                p_state = switchState::FALSE;
                break;

            case switchState::FALSE:  //input confirmed high (released)
                fallingEdge = false; //reset edge event
                if (gpio_get_level(gpio_num) == 0){ //pin low (on)
                    p_state = switchState::LOW;
                    timestampLow = esp_log_timestamp(); //save timestamp switched from high to low
                } else {
                    msReleased = esp_log_timestamp() - timestampHigh; //update duration released
                }
                break;

            case switchState::LOW: //input recently switched to low (pressed)
                if (gpio_get_level(gpio_num) == 0){ //pin still low (on)
                    if (esp_log_timestamp() - timestampLow > minOnMs){ //pin in same state long enough
                        p_state = switchState::TRUE;
                        state = true;
                        risingEdge = true;
                        msReleased = timestampLow - timestampHigh; //calculate duration the button was released
                    }
                }else{
                    p_state = switchState::FALSE;
                }
                break;

            case switchState::TRUE:  //input confirmed low (pressed)
                risingEdge = false; //reset edge event
                if (gpio_get_level(gpio_num) == 1){ //pin high (off)
                    timestampHigh = esp_log_timestamp();
                    p_state = switchState::HIGH;
                } else {
                    msPressed = esp_log_timestamp() - timestampLow; //update duration pressed
                }

                break;

            case switchState::HIGH: //input recently switched to high (released)
                if (gpio_get_level(gpio_num) == 1){ //pin still high (off)
                    if (esp_log_timestamp() - timestampHigh > minOffMs){ //pin in same state long enough
                        p_state = switchState::FALSE;
                        msPressed = timestampHigh - timestampLow; //calculate duration the button was pressed
                        state=false;
                        fallingEdge=true;
                    }
                }else{
                    p_state = switchState::TRUE;
                }
                break;
        }
    }

}