/**
 * @file main.cpp
 * @author Radim Buráň (xburan03@stud.fit.vutbr.cz)
 */

#include "esp_wifi.h"
#include "freertos/event_groups.h"
#include "esp_wpa2.h"
#include "esp_event.h"
#include "esp_system.h"
#include "esp_netif.h"
#include "nvs_flash.h"
#include "esp_log.h"
#include "driver/gpio.h"
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#include "freertos/task.h"
#include "esp_timer.h"

#include "utility.hpp"
#include "wifiswitcher.hpp"

#include "mqtt.hpp"
#include "webserver.hpp"
#include "sensors.hpp"
#include "actuators.hpp"

extern "C" {

/////// INT & LED
int ledStatus = 0;
static QueueHandle_t interruptQueue;

uint32_t lastPress = 0;
uint32_t PRESS_DELAY = 500000L;

EventGroupHandle_t STATUS_GROUP;

presetData_t PRESET_DATA;

void IRAM_ATTR button_interrupt_handler(void* arg) {
    int gpio_num = (int) arg;
    xQueueSendFromISR(interruptQueue, &gpio_num, NULL);
}

void taskButtonInterrupt(void* params) {
    int pin;
    while(1) {
        if (xQueueReceive(interruptQueue, &pin, portMAX_DELAY)) {
            if (lastPress + PRESS_DELAY < esp_timer_get_time()) {
                ledStatus = !ledStatus;
                gpio_set_level(LED_PIN, ledStatus);
                if (ledStatus) {
                    switchToAP();
                }
                else {
                    switchToSTA();
                }
            }
            lastPress = esp_timer_get_time();
        }
    }
}

///////// INT & LED

uint8_t connectCounter = 0;

////// WIFI
void wifi_event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) {
    switch (event_id) {
        case WIFI_EVENT_STA_DISCONNECTED:
            ++connectCounter;
            if (connectCounter >= 5) {
                xEventGroupSetBits(STATUS_GROUP, BIT_OFFLINE_MODE);
                sensorsStart(false);
                break;
            }
            [[fallthrough]];
        case WIFI_EVENT_STA_START:
            ESP_LOGI(LOG_TAG, "Connecting to WiFi...");
            esp_wifi_connect();
            break;
        case WIFI_EVENT_STA_STOP:
            ESP_LOGI(LOG_TAG, "WiFi STA stopped!");
            xEventGroupClearBits(STATUS_GROUP, BIT_WIFI_ESTABLISHED);
            mqttStop();
            break;
        case WIFI_EVENT_STA_CONNECTED:
            ESP_LOGI(LOG_TAG, "Connected to the WiFi");
            break;
        case IP_EVENT_STA_GOT_IP: {
            ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data;
            ESP_LOGI(LOG_TAG, "WiFi STA got IP! -> " IPSTR, IP2STR(&event->ip_info.ip));
            xEventGroupSetBits(STATUS_GROUP, BIT_WIFI_ESTABLISHED);
            sensorsStart(true);
            mqttStart();
            break;
        }

        case WIFI_EVENT_AP_START:
            ESP_LOGI(LOG_TAG, "WiFi AP started!");
            webServerStart();
            break;
        case WIFI_EVENT_AP_STOP:
            ESP_LOGI(LOG_TAG, "WiFi AP stopped!");
            webServerStop();
            break;

        default:
            break;
    }
}

void wifiInit() {
    esp_netif_init();
    esp_event_loop_create_default();
    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    esp_wifi_init(&cfg);
    esp_wifi_set_mode(WIFI_MODE_NULL);

    esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &wifi_event_handler, NULL);
    esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &wifi_event_handler, NULL);
    
    esp_wifi_set_storage(WIFI_STORAGE_RAM);
}

void app_main() {
    nvs_flash_init();
    ESP_LOGI(LOG_TAG, "Start!");

    gpio_set_direction(LED_PIN, GPIO_MODE_OUTPUT);
    gpio_set_direction(PUMP_PIN, GPIO_MODE_OUTPUT);

    wifiInit();

    if (esp_sleep_get_wakeup_cause() == ESP_SLEEP_WAKEUP_EXT0) {
        ESP_LOGI(LOG_TAG, "Button wakeup!");
        switchToAP();
        return; //only webserver, do not execute other code below
    }

    STATUS_GROUP = xEventGroupCreate();
    
    interruptQueue = xQueueCreate(10, sizeof(int));
    xTaskCreate(taskButtonInterrupt, "taskButtonInterrupt", 2048, NULL, 1, NULL);

    gpio_set_direction(BUTTON_PIN, GPIO_MODE_INPUT);
    gpio_set_pull_mode(BUTTON_PIN, GPIO_PULLUP_ONLY);
    gpio_set_intr_type(BUTTON_PIN, GPIO_INTR_NEGEDGE);  //falling edge

    gpio_install_isr_service(0);
    gpio_isr_handler_add(BUTTON_PIN, button_interrupt_handler, (void*)BUTTON_PIN);

    ////////////////////// WiFi & MQTT ////////////////////////

    switchToSTA();

}

}