#include "meteofrance.h"

#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
#include "esp_err.h"
#include "esp_log.h"
#include "esp_check.h"
#include "esp_event.h"
#include "nvs_flash.h"
#include <locale.h>
#include <stdio.h>
#include <math.h>


#include "esp_http_server.h"


#include "main.h"
#include "ihm.h"
#include "ihm_gateway.h"

// OTA
/*#include "esp_ota_ops.h"
#include "esp_http_client.h"
#include "esp_https_ota.h"
*/
// Includes personnels
//#include "wifi_logger.h"
#include "obtain_time.h"
#include "image_downloader.h"
#include "include/communication.h"
#include "stateManagement.h"
//#include "driver/gpio.h"
//#include "am2302_rmt.h"
#include "eventsManager.h"

#include "esp_timer.h"

#include <esp_task_wdt.h>

// GPIO assignment
#define AM2302_GPIO  4

//#include "esp_littlefs.h"

#define MOUNT_POINT "/sdcard"
// Pin assignments can be set in menuconfig, see "SD SPI Example Configuration" menu.
// You can also change the pin assignments here by changing the following 4 lines.
#define PIN_NUM_MISO 13
#define PIN_NUM_MOSI 11
#define PIN_NUM_CLK 12
#define PIN_NUM_CS 10

#define I2C_MASTER_SCL_IO 32      /*!< gpio number for I2C master clock */
#define I2C_MASTER_SDA_IO 36      /*!< gpio number for I2C master data  */
#define I2C_MASTER_NUM I2C_NUM_0  /*!< I2C port number for master dev */
#define I2C_MASTER_FREQ_HZ 100000 /*!< I2C master clock frequency */

//static bh1750_handle_t bh1750 = NULL;

static const char *TAG = "domoTic";





#define WIFI_RDY 0b0001



static void wifiStatus_obs_cb(lv_observer_t * observer, lv_subject_t * subject);



lv_subject_t mqttStatus;
//lv_subject_t wifiStatus;

extern lv_subject_t tempIntSubj;
extern lv_subject_t tempExtSubj;
extern lv_subject_t hauteurCuveSubj;
extern lv_subject_t hauteurCuveEvolSubj;

void init_display(){
/*    lvgl_port_cfg_t lvgl_cfg = ESP_LVGL_PORT_INIT_CONFIG();
    lvgl_cfg.task_priority=15;
    bsp_display_cfg_t cfg = {
        .lvgl_port_cfg = lvgl_cfg,
        .buffer_size = 1024*600,//BSP_LCD_DRAW_BUFF_SIZE,
        .double_buffer = 1,
        .flags = {
            .buff_dma = false,
            .buff_spiram = false,
            .sw_rotate = true
        }
    };
    ESP_LOGE(TAG,"On demarre le display");
    lv_display_t *dsp = bsp_display_start_with_config(&cfg);
    //bsp_display_rotate(dsp,LV_DISP_ROTATION_180);

    bsp_display_backlight_on();
    bsp_display_brightness_set(50);
    
    mainState.display_init=true;
    */
}

/**
 * @brief i2c master initialization
 */
/* static void i2c_bus_init(i2c_master_bus_handle_t *bus_handle)
{
    // i2c_master_bus_config_t conf;
    // conf.sda_io_num = (gpio_num_t)I2C_MASTER_SDA_IO;
    // conf.flags.enable_internal_pullup=true;
    // conf.scl_io_num = (gpio_num_t)I2C_MASTER_SCL_IO;
    // conf.i2c_port=I2C_NUM_0;
    // conf.clk_source = I2C_CLK_SRC_DEFAULT;
    // conf.glitch_ignore_cnt = 7;
    // ESP_ERROR_CHECK(i2c_new_master_bus(&conf, bus_handle));
}
 */
void bh1750_init(void)
{
    /*
    i2c_master_bus_handle_t bus_handle;
    i2c_bus_init(&bus_handle);
    bh1750 = bh1750_create(I2C_MASTER_NUM, BH1750_I2C_ADDRESS_DEFAULT, bus_handle);
    */
}


void mqtt_cb(mqtt_evt evt, esp_mqtt_event_handle_t event){
    switch (evt)
    {
    case MQTT_CONNECTED:
        //if(lvgl_port_lock(50)){
            ESP_LOGV(TAG,"Statut mqttStatus 1");
            //lv_subject_set_int(&mqttStatus,1);
            //lvgl_port_unlock();
        //}
        break;
    case MQTT_DISCONNECTED:
        //if(lvgl_port_lock(50)){
            ESP_LOGE(TAG,"Statut mqttStatus 0");
            //lv_subject_set_int(&mqttStatus,0);
            //lvgl_port_unlock();
        //}
        esp_mqtt_client_reconnect(event->client);
        break;
    case MQTT_DATA_RECEIVED:
        //lv_subject_set_int(&mqttStatus,2);
        ESP_LOGD(TAG, "\nMQTT_EVENT_DATA");
        ESP_LOGD(TAG, "TOPIC=%.*s\n", event->topic_len, event->topic);
        ESP_LOGD(TAG, "DATA=%.*s\n", event->data_len, event->data);
        char *topic = strndup(event->topic, event->topic_len);
        if (strcmp(topic, topicTempExt) == 0)
        {
            //if(lvgl_port_lock(50)){
            float temp = strtof(event->data, NULL);
            char buff[5];
            sprintf(buff,"%.1f",temp);
            //lv_subject_copy_string(&tempExtSubj, buff);
            
            //lvgl_port_unlock();
            //}
        }
        else if (strcmp(topic, topicTempInt) == 0)
        {
            //if(lvgl_port_lock(0)){
            // on retransforme en float pour ne garder que la partie entiere de la température
            float temp = strtof(event->data, NULL);
            char buff[5];
            sprintf(buff,"%.1f",temp);
            //lv_subject_copy_string(&tempIntSubj, buff);
            //lvgl_port_unlock();
            //}
        }
        /*else if (strncmp(event->topic, topicHauteurCuveEvol, event->topic_len) == 0)
        {
            float datas[4] = {};
            splitIt(event->data, event->data_len, datas);
            ser1->y_points[0] = 130 - (int)datas[0];
            ser1->y_points[1] = 130 - (int)datas[1];
            ser1->y_points[2] = 130 - (int)datas[2];
            ser1->y_points[3] = 130 - (int)datas[3];
            lvgl_port_lock(0);
            lv_chart_refresh(chart);
            lvgl_port_unlock();
            

            // lv_subject_copy_string(&hauteurCuveEvolSubj, event->data);
        }*/
        else if (strcmp(topic, topicHauteurCuve) == 0)
        {
            char* datas = malloc(event->data_len+1);
            strncpy(datas, event->data, event->data_len);
            float f = strtof(datas, NULL);
            free(datas);
            ESP_LOGE(TAG,"%f recu mqtt", f);
            send_event(EVT_HAUTEUR_CUVE,&f);
           /*
           if (lvgl_port_lock(50)){
                float temp = strtof(event->data, NULL);
                char buff[5];
                sprintf(buff,"%.0f",temp);
                lv_subject_copy_string(&hauteurCuveSubj, buff);
                lvgl_port_unlock();
            }
            */
        }else if (strcmp(topic, topicTest) == 0){
            ESP_LOGD(TAG,"Msg reecu sur test");
        }else if (strcmp(topic, topicConsoElec) == 0){
            char* datas = malloc(event->data_len+1);
            strncpy(datas, event->data, event->data_len);
            int puissanceRecue = atoi(datas);
            free(datas);
            ESP_LOGE(TAG, "MQTT: Puissance recue %i", puissanceRecue);
            send_event(EVT_PUISSANCE_RECUE,&puissanceRecue);

        }else if (strcmp(topic, topicEtatMachine) == 0){
            char* datas = malloc(event->data_len+1);
            strncpy(datas, event->data, event->data_len);
            
            send_event(EVT_ETAT_MACHINE,datas);

        }else if (strcmp(topic, topicdomoticCommand) == 0){
            char *datas = strndup(event->data, event->data_len);
            if(strcmp(datas,"restart")==0){
                ESP_LOGI(TAG," Commande 'restart' recue");
                //esp_restart();
            }
        }
        else
        {
            ESP_LOGE(TAG, "None match :-( %s", topic);
        }
        free(topic);
        //if (lvgl_port_lock(50)){
            //lv_subject_set_int(&mqttStatus,3);
            //lvgl_port_unlock();
        //}

        break;
    default:
        break;
    }
}



struct state mainState={
    .wifi_init=false,
    .display_init=false
};

void mount_sd_card()
{
/*     // Options for mounting the filesystem.
    // If format_if_mount_failed is set to true, SD card will be partitioned and
    // formatted in case when mounting fails.
    esp_vfs_fat_sdmmc_mount_config_t mount_config = {
        .format_if_mount_failed = false,
        .max_files = 5,
        .allocation_unit_size = 16 * 1024};
    sdmmc_card_t *card;
    const char mount_point[] = MOUNT_POINT;
    ESP_LOGI(TAG, "Initializing SD card");

    ESP_LOGI(TAG, "Using SPI peripheral");

    sdmmc_host_t host = SDSPI_HOST_DEFAULT();

    spi_bus_config_t bus_cfg = {
        .mosi_io_num = PIN_NUM_MOSI,
        .miso_io_num = PIN_NUM_MISO,
        .sclk_io_num = PIN_NUM_CLK,
        .quadwp_io_num = -1,
        .quadhd_io_num = -1,
        .max_transfer_sz = 4000,
    };
    esp_err_t ret = spi_bus_initialize(host.slot, &bus_cfg, SDSPI_DEFAULT_DMA);
    if (ret != ESP_OK)
    {
        ESP_LOGE(TAG, "Failed to initialize bus.");
        return;
    }

    sdspi_device_config_t slot_config = SDSPI_DEVICE_CONFIG_DEFAULT();
    slot_config.gpio_cs = PIN_NUM_CS;
    slot_config.host_id = host.slot;

    ESP_LOGI(TAG, "Mounting filesystem");
    ret = esp_vfs_fat_sdspi_mount(mount_point, &host, &slot_config, &mount_config, &card);

    if (ret != ESP_OK)
    {
        if (ret == ESP_FAIL)
        {
            ESP_LOGE(TAG, "Failed to mount filesystem. "
                          "If you want the card to be formatted, set the CONFIG_EXAMPLE_FORMAT_IF_MOUNT_FAILED menuconfig option.");
        }
        else
        {
            ESP_LOGE(TAG, "Failed to initialize the card (%s). "
                          "Make sure SD card lines have pull-up resistors in place.",
                     esp_err_to_name(ret));
        }
        return;
    }
    ESP_LOGI(TAG, "Filesystem mounted");

    // Card has been initialized, print its properties
    sdmmc_card_print_info(stdout, card);
 */}

extern char *days[7];
extern char *months[12];


/* esp_err_t _ota_http_event_handler(esp_http_client_event_t *evt)
{
    switch (evt->event_id) {
    case HTTP_EVENT_ERROR:
        ESP_LOGD(TAG, "HTTP_EVENT_ERROR");
        break;
    case HTTP_EVENT_ON_CONNECTED:
        ESP_LOGD(TAG, "HTTP_EVENT_ON_CONNECTED");
        break;
    case HTTP_EVENT_HEADER_SENT:
        ESP_LOGD(TAG, "HTTP_EVENT_HEADER_SENT");
        break;
    case HTTP_EVENT_ON_HEADER:
        ESP_LOGD(TAG, "HTTP_EVENT_ON_HEADER, key=%s, value=%s", evt->header_key, evt->header_value);
        break;
    case HTTP_EVENT_ON_DATA:
        ESP_LOGD(TAG, "HTTP_EVENT_ON_DATA, len=%d", evt->data_len);
        break;
    case HTTP_EVENT_ON_FINISH:
        ESP_LOGD(TAG, "HTTP_EVENT_ON_FINISH");
        break;
    case HTTP_EVENT_DISCONNECTED:
        ESP_LOGD(TAG, "HTTP_EVENT_DISCONNECTED");
        break;
    case HTTP_EVENT_REDIRECT:
        ESP_LOGD(TAG, "HTTP_EVENT_REDIRECT");
        break;
    }
    return ESP_OK;
}
 */

extern const uint8_t server_cert_pem_start[] asm("_binary_ca_cert_pem_start");
extern const uint8_t server_cert_pem_end[] asm("_binary_ca_cert_pem_end");
/* static esp_err_t validate_image_header(esp_app_desc_t *new_app_info)
{
    if (new_app_info == NULL) {
        return ESP_ERR_INVALID_ARG;
    }

    const esp_partition_t *running = esp_ota_get_running_partition();
    esp_app_desc_t running_app_info;
    if (esp_ota_get_partition_description(running, &running_app_info) == ESP_OK) {
        ESP_LOGI(TAG, "Running firmware version: %s", running_app_info.version);
    }

#ifndef CONFIG_EXAMPLE_SKIP_VERSION_CHECK
    if (memcmp(new_app_info->version, running_app_info.version, sizeof(new_app_info->version)) == 0) {
        ESP_LOGW(TAG, "Current running version is the same as a new. We will not continue the update.");
        return ESP_FAIL;
    }
#endif

    return ESP_OK;
}
 */


void simple_ota_example_task(void *pvParameter)
{
/*             ESP_LOGE(TAG,"En attente connexion wifi");
        // Waiting until either the connection is established (WIFI_CONNECTED_BIT). 
        EventBits_t bits = xEventGroupWaitBits(domotic_event_group,
                                           BIT0,
                                           pdFALSE,
                                           pdFALSE,
                                           portMAX_DELAY);
        if (bits & BIT0){

    esp_err_t ota_finish_err = ESP_OK;
    ESP_LOGI(TAG, "Starting OTA example task");
#ifdef CONFIG_EXAMPLE_FIRMWARE_UPGRADE_BIND_IF
    esp_netif_t *netif = get_example_netif_from_desc(bind_interface_name);
    if (netif == NULL) {
        ESP_LOGE(TAG, "Can't find netif from interface description");
        abort();
    }
    struct ifreq ifr;
    esp_netif_get_netif_impl_name(netif, ifr.ifr_name);
    ESP_LOGI(TAG, "Bind interface name is %s", ifr.ifr_name);
#endif
    esp_http_client_config_t config = {
        .url = "https://192.168.0.28:8070/rgb_lcd.bin",
        .timeout_ms = 30000,
        .buffer_size = 20000,
        .buffer_size_tx = 20000, //TX Buffer, Main Buffer
        .event_handler = _ota_http_event_handler,
        .keep_alive_enable = true,
        .cert_pem = (char *)server_cert_pem_start,
#ifdef CONFIG_EXAMPLE_FIRMWARE_UPGRADE_BIND_IF
        .if_name = &ifr,
#endif
    };

#ifdef CONFIG_EXAMPLE_FIRMWARE_UPGRADE_URL_FROM_STDIN
    char url_buf[OTA_URL_SIZE];
    if (strcmp(config.url, "FROM_STDIN") == 0) {
        example_configure_stdin_stdout();
        fgets(url_buf, OTA_URL_SIZE, stdin);
        int len = strlen(url_buf);
        url_buf[len - 1] = '\0';
        config.url = url_buf;
    } else {
        ESP_LOGE(TAG, "Configuration mismatch: wrong firmware upgrade image url");
        abort();
    }
#endif

#ifdef CONFIG_EXAMPLE_SKIP_COMMON_NAME_CHECK
    config.skip_cert_common_name_check = true;
#endif
    esp_https_ota_config_t ota_config = {
        .http_config = &config,
        .buffer_caps = MALLOC_CAP_INTERNAL,
        .bulk_flash_erase =true
    };
    ESP_LOGI(TAG, "Attempting to download update from %s", config.url);
    esp_https_ota_handle_t https_ota_handle = NULL;
    esp_err_t err = esp_https_ota_begin(&ota_config, &https_ota_handle);
    if (err != ESP_OK) {
        ESP_LOGE(TAG, "ESP HTTPS OTA Begin failed");
        vTaskDelete(NULL);
    }

    esp_app_desc_t app_desc;
    err = esp_https_ota_get_img_desc(https_ota_handle, &app_desc);
    if (err != ESP_OK) {
        ESP_LOGE(TAG, "esp_https_ota_get_img_desc failed");
        goto ota_end;
    }
    err = validate_image_header(&app_desc);
    if (err != ESP_OK) {
        ESP_LOGE(TAG, "image header verification failed");
        goto ota_end;
    }
        xIHMEvent_t m = {
            .eEventType = IHM_EVT_OTA_STARTED,
            .pvData = NULL
        };
       //xQueueSendToBack(ihm_queue,&m,5);

    esp_task_wdt_config_t cfgWdt = {
        .idle_core_mask = 0,
        .timeout_ms=15000,
        .trigger_panic=false
    };
    esp_task_wdt_reconfigure(&cfgWdt);      

    while (1) {
        err = esp_https_ota_perform(https_ota_handle);
        if (err != ESP_ERR_HTTPS_OTA_IN_PROGRESS) {
            break;
        }
        // esp_https_ota_perform returns after every read operation which gives user the ability to
        // monitor the status of OTA upgrade by calling esp_https_ota_get_image_len_read, which gives length of image
        // data read so far.
        //ESP_LOGE(TAG,"Image size : %i", esp_https_ota_get_image_size(https_ota_handle));
        int percent=esp_https_ota_get_image_len_read(https_ota_handle)*100/esp_https_ota_get_image_size(https_ota_handle);
        //ESP_LOGE(TAG, "Image bytes read: %d %i%%", esp_https_ota_get_image_len_read(https_ota_handle),percent);
        m.eEventType=IHM_EVT_OTA_PROGRESS;
        m.pvData=(void*)percent;
        //xQueueSendToBack(ihm_queue,&m,5);
        vTaskDelay(10/portTICK_PERIOD_MS);
    }
    if (esp_https_ota_is_complete_data_received(https_ota_handle) != true) {
        // the OTA image was not completely received and user can customise the response to this situation.
        ESP_LOGE(TAG, "Complete data was not received.");
    } else {
        ota_finish_err = esp_https_ota_finish(https_ota_handle);
        if ((err == ESP_OK) && (ota_finish_err == ESP_OK)) {
            ESP_LOGI(TAG, "ESP_HTTPS_OTA upgrade successful. Rebooting ...");
            vTaskDelay(1000 / portTICK_PERIOD_MS);
            esp_restart();
        } else {
            if (ota_finish_err == ESP_ERR_OTA_VALIDATE_FAILED) {
                ESP_LOGE(TAG, "Image validation failed, image is corrupted");
            }
            ESP_LOGE(TAG, "ESP_HTTPS_OTA upgrade failed 0x%x", ota_finish_err);
            vTaskDelete(NULL);
        }
    }
ota_end:
    esp_https_ota_abort(https_ota_handle);
    ESP_LOGE(TAG, "ESP_HTTPS_OTA upgrade failed");
    vTaskDelete(NULL);
        }
 */}

//am2302_handle_t sensor = NULL;

void readTempHumid(void *pvParameter)
{
/*     float temperature = 0;
    float humidity = 0;
    while (1)
    {
        am2302_read_temp_humi(sensor, &temperature, &humidity);
        char buff[40];
        ESP_LOGI(TAG, "Temperature: %.1f °C, Humidity: %.1f %%", temperature, humidity);
        sprintf(buff,"%.1f °C, %.1f %%", temperature, humidity);
        xIHMEvent_t m = {
            .eEventType = IHM_EVT_HUMID_TEMP,
            .pvData = buff
        };
        //xQueueSendToFront(ihm_queue,&m,5);
        vTaskDelay(60000 / portTICK_PERIOD_MS);
    }
 */    
}

void alloc_fail(size_t size, uint32_t caps, const char * function_name){
    ESP_LOGE(TAG,"fail alloc %u in %" PRIu32 " in %s", size,caps,function_name);

}
static QueueHandle_t gpio_evt_queue = NULL;
// Durée pour l'arret automatique (micro-secondes ^^)
uint64_t arretAuto=5*60*1000*1000;
bool ecranEteint=true;
// Ce timer permet d'eteindre l'ecran "arretAuto" apres la derniere présence détectée
esp_timer_handle_t presence_timer;

/* static void IRAM_ATTR gpio_isr_handler(void* arg)
{
    uint32_t gpio_num = (uint32_t) arg;
    xQueueSendFromISR(gpio_evt_queue, &gpio_num, NULL);

}
 */
float maxBrightness = 100;

/* static void gpio_task_example(void* arg)
{
    uint32_t io_num;
    int delay=50;
    for (;;) {
        if (xQueueReceive(gpio_evt_queue, &io_num, portMAX_DELAY)) {
            ESP_LOGE(TAG,"Got it !");
            if(ecranEteint){
                    for (int i = 0; i < maxBrightness; i+=2)
                    {
                        if(bsp_display_lock(0)){
                            bsp_display_brightness_set(i);
                            bsp_display_unlock();
                        }   
                        vTaskDelay(delay/portTICK_PERIOD_MS);
                    }
                    ecranEteint=false;
            }else{
                ESP_LOGI(TAG, "Ecran déjà allumé");
            }
            //On arrete le timer de presence
            esp_timer_stop(presence_timer);
            //Pour le redemarrer
            ESP_ERROR_CHECK(esp_timer_start_once(presence_timer, arretAuto));
       }
    }
}
 */#define GPIO_INPUT_IO_0     CONFIG_GPIO_INPUT_CAPTEUR_PIR

void initPirSensor(){
/*     //create a queue to handle gpio event from isr
    gpio_evt_queue = xQueueCreate(10, sizeof(uint32_t));
    //start gpio task
    xTaskCreate(gpio_task_example, "gpio_task_example", 5000, NULL, 10, NULL);

    gpio_config_t gpioconf={
        .pin_bit_mask= 1ULL<<GPIO_INPUT_IO_0,
        .intr_type=GPIO_INTR_POSEDGE,
        .mode=GPIO_MODE_INPUT,
        .pull_up_en=1
    };
    ESP_ERROR_CHECK(gpio_config(&gpioconf));
    gpio_install_isr_service(0);
    gpio_isr_handler_add(GPIO_INPUT_IO_0,gpio_isr_handler,(void*)(GPIO_INPUT_IO_0));
 */
}

/* Ce timer permet d'eteindre l'ecran apres @arretAuto de présence*/
/* static void presence_timer_callback(void* arg)
{
    int64_t time_since_boot = esp_timer_get_time();
    for (int i = maxBrightness; i >= 0; i-=2)
    {
        if(bsp_display_lock(0)){
            bsp_display_brightness_set(i);
            bsp_display_unlock();
        }
        vTaskDelay(20/portTICK_PERIOD_MS);
    }
    ecranEteint=true;
}
 */
static esp_err_t download_get_handler(httpd_req_t *req) {
  httpd_resp_set_type(req, "application/octet-stream");
  httpd_resp_set_hdr(req, "Content-Disposition",
                     "attachment;filename=core.bin");

  esp_partition_iterator_t partition_iterator = esp_partition_find(
      ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_COREDUMP, "coredump");

  const esp_partition_t *partition = esp_partition_get(partition_iterator);

  int file_size = 65536;
  int chunk_size = 1024;
  int i = 0;
  for (i = 0; i < (file_size / chunk_size); i++) {
    char store_data[chunk_size];
    ESP_ERROR_CHECK(
        esp_partition_read(partition, i * chunk_size, store_data, chunk_size));
    httpd_resp_send_chunk(req, store_data, chunk_size);
  }
  uint16_t pending_size = file_size - (i * chunk_size);
  char pending_data[pending_size];
  if (pending_size > 0) {
    ESP_ERROR_CHECK(esp_partition_read(partition, i * chunk_size, pending_data,
                                       pending_size));
    httpd_resp_send_chunk(req, pending_data, pending_size);
  }
  httpd_resp_send_chunk(req, NULL, 0);
  return ESP_OK;
}


extern const unsigned char index_start[] asm("_binary_index_html_start");
extern const unsigned char index_end[] asm("_binary_index_html_end");

static esp_err_t crash_get_handler(httpd_req_t *req) {
  const size_t index_size = (index_end - index_start);
  httpd_resp_set_type(req, "text/html");
  httpd_resp_send_chunk(req, (const char *)index_start, index_size);
  httpd_resp_send_chunk(req, NULL, 0);
  assert(0);
  return ESP_OK;
}

static const httpd_uri_t download = {
    .uri = "/download",
    .method = HTTP_GET,
    .handler = download_get_handler,
    .user_ctx = NULL,
};

static const httpd_uri_t crash = {
    .uri = "/crash",
    .method = HTTP_GET,
    .handler = crash_get_handler,
    .user_ctx = NULL,
};
static esp_err_t root_get_handler(httpd_req_t *req) {
  const size_t index_size = (index_end - index_start);
  httpd_resp_set_type(req, "text/html");
  httpd_resp_send_chunk(req, (const char *)index_start, index_size);
  httpd_resp_send_chunk(req, NULL, 0);
  return ESP_OK;
}
static const httpd_uri_t root = {
    .uri = "/",
    .method = HTTP_GET,
    .handler = root_get_handler,
    .user_ctx = NULL,
};



static httpd_handle_t start_webserver(void) {
  httpd_handle_t server = NULL;
  httpd_config_t config = HTTPD_DEFAULT_CONFIG();
  config.max_resp_headers = 1024;
  config.lru_purge_enable = true;

  // Start the httpd server
  ESP_LOGI(TAG, "Starting server on port: '%d'", config.server_port);
  if (httpd_start(&server, &config) == ESP_OK) {
    // Set URI handlers
    ESP_LOGI(TAG, "Registering URI handlers");
    httpd_register_uri_handler(server, &root);
    httpd_register_uri_handler(server, &download);
    httpd_register_uri_handler(server, &crash);
    return server;
  }

  ESP_LOGI(TAG, "Error starting server!");
  return NULL;
}


void wifi_cb(wifi_evt evt){
    ESP_LOGE(TAG,"On est dans wifi_cb %i", evt);
    switch(evt){
        case WIFI_CONNECTED:
            mainState.wifi_init=true;
            send_event(WIFI_CONNECTED,NULL);
            break;
        case WIFI_DISCONNECTED:
            mainState.wifi_init=true;
            send_event(WIFI_DISCONNECTED,NULL);
            break;
        case WIFI_GOT_IP:
            mainState.wifi_init=true;
            send_event(WIFI_CONNECTED,NULL);
            break;
        case WIFI_CONNECT_FAIL:
            break;
        default:
            break;
    }

}

extern lv_subject_t forecastD1Subj;
extern lv_subject_t forecastD2Subj;
extern lv_subject_t forecastD3Subj;
extern lv_subject_t forecastH1Subj;
extern lv_subject_t forecastH2Subj;
extern lv_subject_t forecastH3Subj;
extern lv_subject_t meteoStatus;

void weather_data_retreived(struct meteodailyforecast_data dailyDatas[3], struct meteoforecast_data datas[3])
{
    
/*    ESP_LOGE(TAG, "debut debug");
    printf("%lld\n", datas[0].datetime);
    printf("%s\n", datas[0].previsions.desc);
    printf("%f\n", datas[0].previsions.value);
    printf("%lld\n", datas[1].datetime);
    printf("%s\n", datas[1].previsions.desc);
    printf("%f\n", datas[1].previsions.value);
    printf("%lld\n", datas[2].datetime);
    printf("%s\n", datas[2].previsions.desc);
    printf("%f\n", datas[2].previsions.value);
    ESP_LOGE(TAG, "fin debug");
*/    
    //if (display_lock("weather_data_retreived"))
    //{
        ESP_LOGV("MeteoFrance", "------------------------------------- Set des subjects J --------------------------------");
        // Prévisions des 3 prochains jours
        //lv_subject_set_pointer(&forecastD1Subj, &dailyDatas[0]);
        //lv_subject_set_pointer(&forecastD2Subj, &dailyDatas[1]);
        //lv_subject_set_pointer(&forecastD3Subj, &dailyDatas[2]);

        ESP_LOGV("MeteoFrance", "------------------------------------- Set des subjects H--------------------------------");
        // Prévisions des 3 prochains jours
        ESP_LOGV("MeteoFrance", "Pointeur %lli", datas[0].datetime);
        //lv_subject_set_pointer(&forecastH1Subj, &datas[0]);
        //lv_subject_set_pointer(&forecastH2Subj, &datas[1]);
       // lv_subject_set_pointer(&forecastH3Subj, &datas[2]);

        //lv_subject_set_int(&meteoStatus, 0);
        //display_unlock("weather_data_retreived");
         ESP_LOGV(TAG, "------------------------------------- Fin Set des subjects --------------------------------");
    //}else{
        ESP_LOGE(TAG, "Impossible d'obtenir le mutex dans weather_data_retreived");
    //}
}

void weather_data_retreived_start()
{
    //if (display_lock("weather_data_retreived_start"))
    //{
        //ESP_LOGE(TAG,"Mutex obtenu dans weather_data_retreived_start");
        //lv_subject_set_int(&meteoStatus, 1);
        ESP_LOGE(TAG,"Subject setted weather_data_retreived_start");
        //display_unlock("weather_data_retreived_start");
    //}else{
    //    ESP_LOGE(TAG,"Impossible d'obtenir le mutex dans weather_data_retreived_start");
    //}
}

LV_IMAGE_DECLARE(mqtt_ok);
LV_IMAGE_DECLARE(mqtt_ko);

/* static void mqttStatus_obs_cb(lv_observer_t * observer, lv_subject_t * subject)
{
    ESP_LOGV(TAG, "On passe dans le callback de chgt de statut; %li", lv_subject_get_int(subject));
    if(lvgl_port_lock(0)){
        lv_obj_t * wifiSt = lv_obj_get_child(lv_obj_get_child(lv_layer_top(), 0),3);
        if(lv_obj_check_type(wifiSt, &lv_image_class)){
            switch (lv_subject_get_int(subject))
            {
            case 0:
                lv_image_set_src(wifiSt,&mqtt_ko);
                break;
            case 1:
                lv_image_set_src(wifiSt,&mqtt_ok);
                break;
            case 2:
                lv_color_t color  = lv_color_make(255, 0, 0);
                lv_obj_set_style_image_recolor_opa(wifiSt, 125, 0);
                lv_obj_set_style_image_recolor(wifiSt, color, 0);
                vTaskDelay(2000 / portTICK_PERIOD_MS);

                break;
            case 3:
                lv_obj_set_style_image_recolor_opa(wifiSt, 0, 0);
                break;
            default:
                break;
            }
        }else{
            ESP_LOGE(TAG, "L'objet recuip en semble pas etre du bon type");
        }
        lvgl_port_unlock();
    }
    //int32_t prev_v = lv_subject_get_previous_int(subject);
    //int32_t cur_v = lv_subject_get_int(subject);

    //lv_obj_t * btn = lv_observer_get_target(observer);
}
 */

typedef struct {
    float Lmin;      // lux min (ex. 1)
    float Lmax;      // lux max (ex. 10000)
    float gamma;     // 0.6 typique
    float pwm_min;   // 0.05 => 5%
    float pwm_max;   // 1.0  => 100%
    float tau_rise;  // s, ex. 1.5
    float tau_fall;  // s, ex. 4.0
    float deadband;  // 0.01 => 1%
    float y;         // état filtré [0..1]
    bool  init;
} auto_bright_t;

static float clampf(float v, float a, float b){ return v < a ? a : (v > b ? b : v); }

float auto_brightness_step(auto_bright_t *ab, float lux, float dt_s)
{
    // 1) lux -> cible en PWM normalisé [0..1]
    lux = fmaxf(lux, ab->Lmin);
    float x = (logf(lux) - logf(ab->Lmin)) / (logf(ab->Lmax) - logf(ab->Lmin));
    x = clampf(x, 0.f, 1.f);
    float b_target = powf(x, ab->gamma);
    float pwm_target = ab->pwm_min + b_target * (ab->pwm_max - ab->pwm_min);
    pwm_target = clampf(pwm_target, ab->pwm_min, ab->pwm_max);

    // init
    if(!ab->init){ ab->y = pwm_target; ab->init = true; return ab->y; }

    // deadband
    //if (fabsf(pwm_target - ab->y) < ab->deadband) return ab->y;

    // 2) EMA avec constante de temps asymétrique
    float tau = (pwm_target > ab->y) ? ab->tau_rise : ab->tau_fall;
    float alpha = dt_s / (tau + dt_s);
    ab->y = (1.f - alpha) * ab->y + alpha * pwm_target;

    return ab->y; // valeur à appliquer (0..1)
}

auto_bright_t ab = {
    .Lmin=0.1f, .Lmax=600.f, .gamma=2.f,
    .pwm_min=0.00062f, .pwm_max=1.0f,
    .tau_rise=1.5f, .tau_fall=4.0f,
    .deadband=0.01f, .init=false
};

int32_t map_value(int32_t x, int32_t in_min, int32_t in_max, int32_t out_min, int32_t out_max) {
    return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}

void lightSensorTask(void *pvParameter){
/*     float bh1750_data;
    while(1){
        ESP_ERROR_CHECK(bh1750_get_data(bh1750, &bh1750_data));
        //maxBrightness = map_value(bh1750_data, 0,300,0, 100);
        maxBrightness = auto_brightness_step(&ab, bh1750_data, 0.1)*100;
        //ESP_LOGI(TAG, "bh1750 val(continuously mode): %f --> %f\n", bh1750_data, maxBrightness);

        int len = snprintf(NULL, 0, "%f", bh1750_data);
        char *result = malloc(len + 1);
        snprintf(result, len + 1, "%f", bh1750_data);
        //mqtt_publish("/domotic/sensor/light", result);
        free(result);
        bsp_display_brightness_set(maxBrightness);
        vTaskDelay(pdMS_TO_TICKS(100));
    }
 */
}

void app_main(void)
{

    ESP_ERROR_CHECK(esp_event_loop_create_default());

    ihm_gateway_init();
    startEvtManager();

    boucleMeteo();
    #if CONFIG_IDF_TARGET_LINUX
    // en mode linux on simule l'obtention de la connexion wifi
    xEventGroupSetBits(domotic_event_group, WIFI_CONNECTED_BIT);

    drawIhm(NULL);
    #else
    xTaskCreatePinnedToCore(&drawIhm,"ihm_task",10000,getIHMQueueHandle(),10,NULL,0);
    #endif

    TaskHandle_t xHandle = NULL;
    BaseType_t ret1;
    ret1 = xTaskCreate(&imgdwn, "imageDownload_task", 3 * 1024, domotic_event_group, 5, &xHandle);
    if (ret1 != pdPASS)
    {
        ESP_LOGE(TAG, "Impossiblke de creer la tache imageDownload_task %i", ret1);
    }


        /* Create widgets on the screen */
        //lv_demo_widgets();
}

void boucleMeteo()
{
    on_weather_data_retrieval(weather_data_retreived);
    on_weather_data_retrieval_start(weather_data_retreived_start);
    ESP_LOGW(TAG, "Weather data retrieval initialized");
    initialise_weather_data_retrieval(600000, domotic_event_group);
}

void app_main1(void)
{
    //esp_task_wdt_deinit();         // désactive le task watchdog
    //vTaskDelay(pdMS_TO_TICKS(10000)); // laisse le temps d'attacher GDB

    //bh1750_measure_mode_t cmd_measure;


    startEvtManager();
    init_display();
/*     const esp_timer_create_args_t periodic_timer_args = {
        .callback = &presence_timer_callback,
        // name is optional, but may help identify the timer when debugging
        .name = "presence"
    };
    ESP_ERROR_CHECK(esp_timer_create(&periodic_timer_args, &presence_timer));
    // Start the timers
    ESP_ERROR_CHECK(esp_timer_start_once(presence_timer, 30*1000*1000));
 */

    initPirSensor();

    esp_log_level_set("wifi", ESP_LOG_ERROR);
    esp_log_level_set(TAG, ESP_LOG_VERBOSE);

    //mount_sd_card();
/*     bsp_sdcard_mount();

   ESP_LOGI(TAG, "Initializing LittleFS");

    esp_vfs_littlefs_conf_t conflfs = {
        .base_path = "/littlefs",
        .partition_label = "littlefs",
        .format_if_mount_failed = true,
        .dont_mount = false,
    };
    // Use settings defined above to initialize and mount LittleFS filesystem.
    // Note: esp_vfs_littlefs_register is an all-in-one convenience function.
     esp_err_t retlfs = esp_vfs_littlefs_register(&conflfs);

    if (retlfs != ESP_OK){
        if (retlfs == ESP_FAIL){
            ESP_LOGE(TAG, "Failed to mount or format filesystem");
        }else if (retlfs == ESP_ERR_NOT_FOUND){
            ESP_LOGE(TAG, "Failed to find LittleFS partition");
        }else{
            ESP_LOGE(TAG, "Failed to initialize LittleFS (%s)", esp_err_to_name(retlfs));
        }
        return;
    }

    size_t total = 0, used = 0;
    retlfs = esp_littlefs_info(conflfs.partition_label, &total, &used);
    if (retlfs != ESP_OK)
    {
        ESP_LOGE(TAG, "Failed to get LittleFS partition information (%s)", esp_err_to_name(retlfs));
    }
    else
    {
        ESP_LOGI(TAG, "Partition size: total: %d, used: %d", total, used);
    }

 */    // On affiche au plus tot l'ecran de démarrage
    // ESP_ERROR_CHECK(esp_lcd_panel_mirror(lcd_panel,true,true));
     xTaskCreatePinnedToCore(&drawIhm,"ihm_task",10000,getIHMQueueHandle(),10,NULL,0);



    // Initialize NVS
    esp_err_t ret = nvs_flash_init();
    if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND)
    {
        ESP_ERROR_CHECK(nvs_flash_erase());
        ret = nvs_flash_init();
    }
    ESP_ERROR_CHECK(ret);

    ESP_LOGI(TAG, "ESP_WIFI_MODE_STA");
    //wifi_init_sta(wifi_cb);
    //start_wifi_logger();
    //wifi_log_e("test", "%s %d %f", "hello world wifi logger", 43, 45.341223242); // write log over wifi with log level -> ERROR
    esp_log_level_set("tcp_handler", ESP_LOG_NONE);
    printf("8b - Free heap after buffers allocation: %zu\n", xPortGetFreeHeapSize());

    printf("9 - Free heap after buffers allocation: %zu\n", xPortGetFreeHeapSize());
    
    /* Ensure to disable any WiFi power save mode, this allows best throughput
     * and hence timings for overall OTA operation.
     */
    //esp_wifi_set_ps(WIFI_PS_NONE);
    //xTaskCreatePinnedToCore(&simple_ota_example_task, "ota__task", 8192, NULL, 6, NULL,0);

    on_weather_data_retrieval(weather_data_retreived);
    on_weather_data_retrieval_start(weather_data_retreived_start);
     ESP_LOGW(TAG, "Weather data retrieval initialized");
    initialise_weather_data_retrieval(600000, domotic_event_group);
    TaskHandle_t xHandle = NULL;
    BaseType_t ret1;
    ret1 = xTaskCreate(&imgdwn, "imageDownload_task", 3 * 1024, domotic_event_group, 5, &xHandle);
    if (ret1 != pdPASS)
    {
        ESP_LOGE(TAG, "Impossiblke de creer la tache imageDownload_task %i", ret1);
    }


    //mqtt_app_start(mqtt_cb, domotic_event_group);
    
    //start_wifi_logger();
    //wifi_log_e("test", "%s %d %f", "hello world wifi logger", 43, 45.341223242); // write log over wifi with log level -> ERROR    }

    //lv_subject_init_int(&mqttStatus,-1);
    //lv_subject_add_observer_obj(&mqttStatus, mqttStatus_obs_cb, NULL, NULL);

    while(!mainState.wifi_init){
        vTaskDelay(pdTICKS_TO_MS(10));
    }
    start_webserver();


/*     // Configuration de la sonde Temp/Humid.
        am2302_config_t am2302_config = {
        .gpio_num = AM2302_GPIO,
    };
    am2302_rmt_config_t rmt_config = {
        .clk_src = RMT_CLK_SRC_DEFAULT,
    };
    ESP_ERROR_CHECK(am2302_new_sensor_rmt(&am2302_config, &rmt_config, &sensor));
    xTaskCreate(&readTempHumid, "read_temp_task", 8192, NULL, 5, NULL);
 */
}
