/*
 weather.c - Weather data retrieval from api.openweathermap.org

 This file is part of the ESP32 Everest Run project
 https://github.com/krzychb/esp32-everest-run

 Copyright (c) 2016 Krzysztof Budzynski <krzychb@gazeta.pl>
 This work is licensed under the Apache License, Version 2.0, January 2004
 See the file LICENSE for details.
*/

#include "meteofrance.h"

#include "esp_system.h"
#include "esp_log.h"

#include <stdio.h>
#include <string.h>

#include "cJSON.h"
#include "esp_http_client.h"
#include "esp_tls.h"
#include "stateManagement.h"
static const char *TAG = "MeteoFrance";

/* Constants that aren't configurable in menuconfig
   Typically only LOCATION_ID may need to be changed
 */
#define WEB_SERVER "webservice.meteofrance.com" //"192.168.0.10" //"www.example.com"// //"webservice.meteofrance.com"
#define WEB_PORT 443                             // 5403 //80
#define WEB_URL "/forecast"
#define TOKEN "__Wj7dVSTjV9YGu1guveLyDq0g7S7TfTjaHBTPTpO0kj8__"
// Location ID to get the weather data for
// #define LOCATION_ID "756135"
#define LATITUDE "49.22017054145735"
#define LONGITUDE "3.92188756221628"
#define WEB_QUERY "token=" TOKEN "&lat=" LATITUDE "&lon=" LONGITUDE

#define MAX_HTTP_OUTPUT_BUFFER 32000

static weather_data weather;
static struct meteodailyforecast_data dailydatas[5];
static struct meteoforecast_data forecastdatas[5];

void printdftemp(struct dailyforecast_prev *tmp)
{
    printf(" Temps Min:%f, Max:%f", tmp->min, tmp->max);
}
void printftemp(struct forecast_prev *tmp)
{
    printf(" Temps Value:%f", tmp->value);
}
#define MAX_SIZE 80

void printfdf(struct meteodailyforecast_data *tmp)
{
    char buffer[MAX_SIZE];
    time_t timeToTransform = tmp->datetime;
    dtToString(timeToTransform,buffer);
    //ESP_LOGE(TAG, "%s", strftime_buf);
    printf("IsValid:%s date:%s, Min:%.1f Max:%.1f Desc:%s Icon: %s\n", tmp->isValid ? "true" : "false", buffer, tmp->previsions.min, tmp->previsions.max, tmp->previsions.desc, tmp->previsions.icon);
}

void dtToString(time_t ttt, char* buffer){
    struct tm timeinfo = {0};
    localtime_r(&ttt, &timeinfo);
    strftime(buffer, MAX_SIZE, "%d/%m", &timeinfo);
    //char strftime_buf[64];
    //sprintf(strftime_buf, "%d %d %d", timeinfo.tm_wday, timeinfo.tm_mday, timeinfo.tm_mon + 1);
}

void dtHToString(time_t ttt, char* buffer){
    struct tm timeinfo = {0};
    localtime_r(&ttt, &timeinfo);
    strftime(buffer, MAX_SIZE, "%H:%M", &timeinfo);
    //char strftime_buf[64];
    //sprintf(strftime_buf, "%d %d %d", timeinfo.tm_wday, timeinfo.tm_mday, timeinfo.tm_mon + 1);
}

void printff(struct meteoforecast_data *tmp)
{
    struct tm timeinfo = {0};
    localtime_r(&tmp->datetime, &timeinfo);
    char buffer[MAX_SIZE];
    strftime(buffer, MAX_SIZE, "%c", &timeinfo);
    char strftime_buf[64];
    sprintf(strftime_buf, "%d %d %d", timeinfo.tm_wday, timeinfo.tm_mday, timeinfo.tm_mon + 1);
    ESP_LOGV(TAG, "%s", strftime_buf);
    printf("IsValid:%s date:%s, Temp:%.1f Desc:%s\n", tmp->isValid ? "true" : "false", buffer, tmp->previsions.value, tmp->previsions.desc);
}

esp_err_t _http_event_handler(esp_http_client_event_t *evt)
{
    static char *output_buffer; // Buffer to store response of http request from event handler
    static int output_len;      // Stores number of bytes read
    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);
        // Clean the buffer in case of a new request
        if (output_len == 0 && evt->user_data)
        {
            // we are just starting to copy the output data into the use
            memset(evt->user_data, 0, MAX_HTTP_OUTPUT_BUFFER);
        }
        /*
         *  Check for chunked encoding is added as the URL for chunked encoding used in this example returns binary data.
         *  However, event handler can also be used in case chunked encoding is used.
         */
        if (!esp_http_client_is_chunked_response(evt->client))
        {
            // If user_data buffer is configured, copy the response into the buffer
            int copy_len = 0;
            if (evt->user_data)
            {
                // The last byte in evt->user_data is kept for the NULL character in case of out-of-bound access.
                if(evt->data_len<(MAX_HTTP_OUTPUT_BUFFER - output_len)){
                   copy_len = evt->data_len;
                }else{
                    copy_len=(MAX_HTTP_OUTPUT_BUFFER - output_len);
                }
                if (copy_len)
                {
                    memcpy(evt->user_data + output_len, evt->data, copy_len);
                }
            }
            else
            {
                int content_len = esp_http_client_get_content_length(evt->client);
                if (output_buffer == NULL)
                {
                    // We initialize output_buffer with 0 because it is used by strlen() and similar functions therefore should be null terminated.
                    output_buffer = (char *)calloc(content_len + 1, sizeof(char));
                    output_len = 0;
                    if (output_buffer == NULL)
                    {
                        ESP_LOGE(TAG, "Failed to allocate memory for output buffer");
                        return ESP_FAIL;
                    }
                }
                if(evt->data_len<(content_len - output_len)){
                   copy_len = evt->data_len;
                }else{
                    copy_len=(content_len - output_len);
                }
                if (copy_len)
                {
                    memcpy(output_buffer + output_len, evt->data, copy_len);
                }
            }
            output_len += copy_len;
        }

        break;
    case HTTP_EVENT_ON_FINISH:
        ESP_LOGD(TAG, "HTTP_EVENT_ON_FINISH");
        if (output_buffer != NULL)
        {
            // Response is accumulated in output_buffer. Uncomment the below line to print the accumulated response
            // ESP_LOG_BUFFER_HEX(TAG, output_buffer, output_len);
            free(output_buffer);
            output_buffer = NULL;
        }
        output_len = 0;
        break;
    case HTTP_EVENT_DISCONNECTED:
        ESP_LOGI(TAG, "HTTP_EVENT_DISCONNECTED");
        /*int mbedtls_err = 0;
        esp_err_t err = esp_tls_get_and_clear_last_error((esp_tls_error_handle_t)evt->data, &mbedtls_err, NULL);
        if (err != 0)
        {
            ESP_LOGI(TAG, "Last esp error code: 0x%x", err);
            ESP_LOGI(TAG, "Last mbedtls failure: 0x%x", mbedtls_err);
        }
        if (output_buffer != NULL)
        {
            free(output_buffer);
            output_buffer = NULL;
        }
        output_len = 0;*/
        break;
    case HTTP_EVENT_REDIRECT:
        ESP_LOGD(TAG, "HTTP_EVENT_REDIRECT");
        esp_http_client_set_header(evt->client, "From", "user@example.com");
        esp_http_client_set_header(evt->client, "Accept", "text/html");
        esp_http_client_set_redirection(evt->client);
        break;
    }
    return ESP_OK;
}

char *JSON_Types(int type)
{
    if (type == cJSON_Invalid)
        return ("cJSON_Invalid");
    if (type == cJSON_False)
        return ("cJSON_False");
    if (type == cJSON_True)
        return ("cJSON_True");
    if (type == cJSON_NULL)
        return ("cJSON_NULL");
    if (type == cJSON_Number)
        return ("cJSON_Number");
    if (type == cJSON_String)
        return ("cJSON_String");
    if (type == cJSON_Array)
        return ("cJSON_Array");
    if (type == cJSON_Object)
        return ("cJSON_Object");
    if (type == cJSON_Raw)
        return ("cJSON_Raw");
    return NULL;
}

void JSON_Parse(const cJSON *const root)
{
    // ESP_LOGI(TAG, "root->type=%s", JSON_Types(root->type));
    cJSON *current_element = NULL;
    // ESP_LOGI(TAG, "roo->child=%p", root->child);
    // ESP_LOGI(TAG, "roo->next =%p", root->next);
    int i = 0;

    // Récupération previsions journalieres (15j par défaut mais on en récupere 3)
    cJSON *df = cJSON_GetObjectItem(root, "daily_forecast");
    if (!cJSON_IsArray(df))
    {
        ESP_LOGE(TAG, "Pas de tableau ^^");
    }
    else
    {
        cJSON_ArrayForEach(current_element, df)
        {
            struct meteodailyforecast_data datasT;
            // ESP_LOGI(TAG, "type=%s", JSON_Types(current_element->type));
            cJSON *dt = cJSON_GetObjectItem(current_element, "dt");
            if (cJSON_IsNumber(dt))
            {
                datasT.datetime = dt->valueint;
                cJSON *temps = cJSON_GetObjectItem(current_element, "T");
                datasT.previsions.min = cJSON_GetObjectItem(temps, "min")->valuedouble;
                datasT.previsions.max = cJSON_GetObjectItem(temps, "max")->valuedouble;

                cJSON *weather12 = cJSON_GetObjectItem(current_element, "weather12H");
                strncpy(datasT.previsions.desc, cJSON_GetObjectItem(weather12, "desc")->valuestring, 24);
                strncpy(datasT.previsions.icon, cJSON_GetObjectItem(weather12, "icon")->valuestring, 8);
                datasT.isValid = true;
                // ESP_LOGE(TAG,"Donnees lues");
                // printffd(&datasT);
                dailydatas[i++] = datasT;
                if (i == 3)
                {
                    break;
                }
            }
        }
    }
    // Récupération previsions du jour ("matin/midi/am/soir")
    cJSON *fore = cJSON_GetObjectItem(root, "forecast");
    if(!cJSON_IsArray(fore)){
        ESP_LOGE(TAG, "Pas de tableau forecast");
    }else{
        i=0;
        //On garde les données de 10h 14h 18h
        cJSON_ArrayForEach(current_element, fore){
            struct meteoforecast_data datasT;
            cJSON *dt = cJSON_GetObjectItem(current_element, "dt");
            if (cJSON_IsNumber(dt))
            {
                datasT.datetime = dt->valueint;
                struct tm timeinfo = {0};
                localtime_r(&(datasT.datetime), &timeinfo);
                if(timeinfo.tm_hour==10 || timeinfo.tm_hour==14 || timeinfo.tm_hour==18){
                    cJSON *temps = cJSON_GetObjectItem(current_element, "T");
                    datasT.previsions.value = cJSON_GetObjectItem(temps, "value")->valuedouble;
                    cJSON *weather = cJSON_GetObjectItem(current_element, "weather");
                    strncpy(datasT.previsions.desc, cJSON_GetObjectItem(weather, "desc")->valuestring, 24);
                    strncpy(datasT.previsions.icon, cJSON_GetObjectItem(weather, "icon")->valuestring, 8);
                    datasT.isValid = true;
                    // ESP_LOGE(TAG,"Donnees lues");
                    // printffd(&datasT);
                    forecastdatas[i++] = datasT;
                    printff(&datasT);
                    if (i == 3)
                    {
                        break;
                    }
                }else{
                    //ESP_LOGE(TAG,"heure = %i on ne garde pas...", timeinfo.tm_hour);
                    //char buffer[MAX_SIZE];
                    //strftime(buffer, MAX_SIZE, "%c", &timeinfo);
                    //printf("date:%s %lli", buffer, datasT.datetime);
                }

            }else{
                ESP_LOGI(TAG,"dt n'est pas au format attendu ^^");
            }
        }
        ESP_LOGI(TAG, "Prévisions lues !");

    }
}

static bool process_response_body(const char *body)
{
    cJSON *root = cJSON_Parse(body);
    JSON_Parse(root);
    cJSON_Delete(root);
    return true;
}

static void http_request_task(void* domotic_event_group)
{
    while (1)
    {
        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_LOGE(TAG,"connexion wifi ok");
            ESP_LOGV(TAG, "Début recup méteo --------------------------");
            weather.data_retreived_cb_start(NULL);
            char *local_response_buffer = heap_caps_malloc((MAX_HTTP_OUTPUT_BUFFER + 1) * (sizeof(char)), MALLOC_CAP_SPIRAM);
            // char local_response_buffer[MAX_HTTP_OUTPUT_BUFFER + 1] = {0};
            /**
             * NOTE: All the configuration parameters for http_client must be spefied either in URL or as host and path parameters.
             * If host and path parameters are not set, query parameter will be ignored. In such cases,
             * query parameter should be specified in URL.
             *
             * If URL as well as host and path parameters are specified, values of host and path will be considered.
             */
            esp_http_client_config_t config = {
                .host = WEB_SERVER,
                .port = WEB_PORT,
                .path = WEB_URL,
                .query = WEB_QUERY,
                .event_handler = _http_event_handler,
                .user_data = local_response_buffer, // Pass address of local buffer to get response
                .disable_auto_redirect = false,
                .transport_type = HTTP_TRANSPORT_OVER_SSL,
                .cert_pem = NULL,
                .skip_cert_common_name_check = true
            };
            esp_http_client_handle_t client = esp_http_client_init(&config);
            char url[50];
            esp_http_client_get_url(client, url, 50);
            ESP_LOGE(TAG, "%s", url);

            // GET
            esp_err_t err = esp_http_client_perform(client);
            if (err == ESP_OK)
            {
                ESP_LOGV(TAG, "HTTP GET Status = %d, content_length = %" PRId64,
                        esp_http_client_get_status_code(client),
                        esp_http_client_get_content_length(client));
            }
            else
            {
                ESP_LOGE(TAG, "HTTP GET request failed: %s", esp_err_to_name(err));
            }
            // ESP_LOGE(TAG, "%s",local_response_buffer);

            process_response_body(local_response_buffer);
            heap_caps_free(local_response_buffer);
            esp_http_client_cleanup(client);
            if(dailydatas->isValid){
                ESP_LOGV(TAG, "Données valides on appelle le cb");
                weather.data_retreived_cb(dailydatas, forecastdatas);
                vTaskDelay(weather.retreival_period / portTICK_PERIOD_MS);
            }else{
                //Ca a échoué on recommence dans 30 secondes
                ESP_LOGI(TAG, "Données non valides on attend avant de retenter");
                vTaskDelay(30000 / portTICK_PERIOD_MS);
            }
        }else{

                ESP_LOGI(TAG, "Wifi non connecté");
        }
    }
}

void on_weather_data_retrieval(weather_data_callback data_retreived_cb)
{
    weather.data_retreived_cb = data_retreived_cb;
}

void on_weather_data_retrieval_start(weather_data_callback data_retreived_cb_start)
{
    weather.data_retreived_cb_start = data_retreived_cb_start;
}

void initialise_weather_data_retrieval(unsigned long retreival_period, void* domotic_event_group)
{

    weather.retreival_period = retreival_period;

    // http_client_on_process_chunk(&http_client, process_chunk);
    // http_client_on_disconnected(&http_client, disconnected);
    TaskHandle_t xHandle = NULL;
    BaseType_t ret1 = xTaskCreatePinnedToCore(&http_request_task, "http_meteof", 5 * 1024, domotic_event_group, 5, &xHandle, 0);
    if(ret1!=pdPASS ){
        ESP_LOGE(TAG, "Impossible de creer la tache %"PRIi16, ret1);
    }
    ESP_LOGI(TAG, "HTTP request task started");
}
