#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <esp_log.h>
#include <nvs_flash.h>
#include <sys/param.h>
#include "esp_netif.h"
#include <esp_wifi.h>
#include <esp_system.h>
#include "nvs_flash.h"
#include "esp_ota_ops.h"
#include "http_server.h"
#include "Protocol_User.h"

static const char *TAG = "http_server";

char buf[MAX_OTA_BUFF]; // 接收服务端传来的文件缓存, 必须使用全局变量, 否则会触发看门狗复位, 原理未知
extern void Cache_data(unsigned char* p,uint32_t len);
extern void SetUpgradeFlashSize(uint32_t size,uint32_t len);
extern void SetUpgradeStart(void);//初始状态
static unsigned char softap_ota_start = 0;

httpd_handle_t http_server = NULL;

static esp_err_t index_html_get_handler(httpd_req_t *req)
{
    ESP_LOGI(TAG, "---- 回到起始页");
    httpd_resp_set_status(req, "307 Temporary Redirect");
    httpd_resp_set_hdr(req, "Location", "/");
    httpd_resp_send(req, NULL, 0);
    return ESP_OK;
}

static esp_err_t favicon_get_handler(httpd_req_t *req)
{
    extern const unsigned char favicon_ico_start[] asm("_binary_favicon_ico_start");
    extern const unsigned char favicon_ico_end[] asm("_binary_favicon_ico_end");
    const size_t favicon_ico_size = (favicon_ico_end - favicon_ico_start);
    ESP_LOGI(TAG, "---- 下载网站图标");
    httpd_resp_set_type(req, "image/x-icon");
    httpd_resp_send(req, (const char *)favicon_ico_start, favicon_ico_size);
    return ESP_OK;
}

static esp_err_t http_resp_dir_html(httpd_req_t *req)
{
    extern const unsigned char ota_page_start[] asm("_binary_ota_page_html_start");
    extern const unsigned char ota_page_end[] asm("_binary_ota_page_html_end");
    const size_t ota_page_size = (ota_page_end - ota_page_start);

    ESP_LOGI(TAG, "---- 下载网站起始页");
    httpd_resp_set_type(req, "text/HTML");
    httpd_resp_send(req, (const char *)ota_page_start, ota_page_size);
    return ESP_OK;
}

static esp_err_t download_get_handler(httpd_req_t *req)
{
    const char *uri_get = req->uri;
    ESP_LOGI(TAG, "---- GET URI: 192.168.4.1%s", req->uri);
    if (strcmp(uri_get, "/") == 0)
    {
        return http_resp_dir_html(req);
    }
    else if (strcmp(uri_get, "/favicon.ico") == 0)
    {
        return favicon_get_handler(req);
    }
    else
    {
        ESP_LOGI(TAG, "---- 非预设GET, 不处理, 直接回到起始页");
        return index_html_get_handler(req);
    }

    return ESP_OK;
}

static esp_err_t app_post_handler(httpd_req_t *req)
{
    int ret = 0;
    int recv_block = 0;
    int remaining = req->content_len;
    int total = remaining;
    double percent = 0.0;

    ESP_LOGI(TAG, "---- 应用程序更新, 需接收数据长度(bytes): %d", remaining);
    esp_ota_handle_t app_update_handle = 0;
    const esp_partition_t *app_update_partition = esp_ota_get_next_update_partition(NULL);
    esp_err_t err = esp_ota_begin(app_update_partition, OTA_WITH_SEQUENTIAL_WRITES, &app_update_handle);
    while (remaining > 0)
    {
        /* Read the data for the request */
        if ((ret = httpd_req_recv(req, buf, MIN(remaining, sizeof(buf)))) <= 0)
        {
            if (ret == HTTPD_SOCK_ERR_TIMEOUT)
            {
                /* Retry receiving if timeout occurred */
                continue;
            }
            return ESP_FAIL;
        }

        err = esp_ota_write(app_update_handle, buf, ret);
        if (err != ESP_OK)
        {
            ESP_LOGI(TAG, "---- 写入数据失败, 错误信息:%s", esp_err_to_name(err));
        }
        else
        {
            remaining -= ret;
            recv_block++;
            if ((recv_block % 20) == 0)
            {
                percent = 100.0 - (double)(remaining * 100) / (double)total;
                ESP_LOGI(TAG, "---- 写入OTA升级数据: %.2f%%", percent);
            }
        }

        // 增加20ms延时, 解决CPU0看门狗超时的问题
        vTaskDelay(pdMS_TO_TICKS(20));
    }
    err = esp_ota_end(app_update_handle);
    if (err == ESP_OK)
    {
        esp_err_t err = esp_ota_set_boot_partition(app_update_partition);
        if (err != ESP_OK)
        {
            ESP_LOGI(TAG, "---- 设备启动分区失败, 错误信息:%s", esp_err_to_name(err));
        }
        else
        {
            ESP_LOGI(TAG, "---- 应用程序更新完成, 3秒后自动重启.");
            httpd_resp_send_chunk(req, NULL, 0);
            // delele_tasks();
            // httpd_resp_sendstr(req, "<p>应用程序更新完成,3秒后自动重启.</p>");
            vTaskDelay(pdMS_TO_TICKS(3000));
            //esp_restart();
            SendCmd_ResetComond();

        }
    }

    // End response
    // httpd_resp_sendstr(req, "<p>数据上传完成,但应用更新不成功,请重试.</p>");
    ESP_LOGI(TAG, "---- 数据接收完成, OTA失败, 1秒后自动重启.");
    httpd_resp_send_chunk(req, NULL, 0);
    // delele_tasks();
    vTaskDelay(pdMS_TO_TICKS(1000));
    esp_restart();
    return ESP_OK;
}

// static esp_err_t assert_post_handler(httpd_req_t *req)
// {
//     int ret, remaining = req->content_len;

//     while (remaining > 0)
//     {
//         /* Read the data for the request */
//         if ((ret = httpd_req_recv(req, buf, MIN(remaining, sizeof(buf)))) <= 0)
//         {
//             if (ret == HTTPD_SOCK_ERR_TIMEOUT)
//             {
//                 /* Retry receiving if timeout occurred */
//                 continue;
//             }
//             return ESP_FAIL;
//         }

//         remaining -= ret;

//         // ESP_LOGI(TAG, "=========== RECEIVED DATA ==========");
//         // 数据处理代码
//         // ESP_LOGI(TAG, "====================================");
//     }

//     // End response
//     httpd_resp_send_chunk(req, NULL, 0);
//     return ESP_OK;
// }

// static esp_err_t storage_post_handler(httpd_req_t *req)
// {
//     int ret, remaining = req->content_len;

//     while (remaining > 0)
//     {
//         /* Read the data for the request */
//         if ((ret = httpd_req_recv(req, buf, MIN(remaining, sizeof(buf)))) <= 0)
//         {
//             if (ret == HTTPD_SOCK_ERR_TIMEOUT)
//             {
//                 /* Retry receiving if timeout occurred */
//                 continue;
//             }
//             return ESP_FAIL;
//         }

//         remaining -= ret;

//         // ESP_LOGI(TAG, "=========== RECEIVED DATA ==========");
//         // 数据处理代码
//         // ESP_LOGI(TAG, "====================================");
//     }

//     // End response
//     httpd_resp_send_chunk(req, NULL, 0);
//     return ESP_OK;
// }
static void MSI_Print_mem16(size_t len,char *buf)
{
    for (size_t i = 0; i < len; )
    {
        ESP_LOGI("MSI","%02xh:  %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x",i,\
        buf[i+0],buf[i+1],buf[i+2],buf[i+3],buf[i+4],buf[i+5],buf[i+6],buf[i+7],
        buf[i+8],buf[i+9],buf[i+10],buf[i+11],buf[i+12],buf[i+13],buf[i+14],buf[i+15]);
        i+=16;
    }
} 
/*上传文件名是BAT32A239dat.bin的处理函数*/
static esp_err_t nation_post_handler(httpd_req_t *req)
{
  
    int recv_block = 0;
    double percent = 0.0;
    int ret, remaining = req->content_len;
    int total = remaining;
    int err_timeout_retry_cnt = 0;

    size_t erase_size = 0;
    size_t erase_offset = 0;
    size_t write_offset = 0;

    int Addr = 0;

    esp_err_t err = ESP_OK;
    const char *partition_label = "assert";
    const esp_partition_t *assert_update_partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, partition_label);

      // if (Sys_Run_hdl != NULL)
    // {
    //     vTaskDelete(Sys_Run_hdl);
    //     Sys_Run_hdl = NULL;
    //     vTaskDelay(pdMS_TO_TICKS(100));
    // }

    //SetUpgradeFlashSize(remaining); // 设定文件大小
    
    ESP_LOGI(TAG, "---- 国民技术程序文件: 需要接收的数据(byte) = %d", remaining);
    while (remaining > 0)
    {
        /* Read the data for the request */
        if ((ret = httpd_req_recv(req, buf, MIN(remaining, sizeof(buf)))) <= 0)
        {
            if (ret == HTTPD_SOCK_ERR_TIMEOUT)
            {
                err_timeout_retry_cnt++;
                if (err_timeout_retry_cnt >= MAX_RETRY_COUNT)
                {
                    httpd_resp_send_408(req);
                    httpd_resp_send_chunk(req, NULL, 0);
                    ESP_LOGE(TAG, "---- 接收数据超时, 退出升级流程, 请保持无线连接稳定后再试.");
                    return ESP_FAIL;
                }
                /* else
                {
                    Retry receiving if timeout occurred 
                    
                            continue;
                }*/
            }
            else
            {
                ESP_LOGI("MSI","total : %d  \n",total);
                ESP_LOGI("MSI","remaining : %d  \n",remaining);
                
               err_timeout_retry_cnt = 0; 
            }
        }
        if(total == remaining)//下载长度+地址
        {
            Addr = 0;
            Addr |= buf[8];
            Addr <<= 8;
            Addr |= buf[9];
            Addr <<= 8;
            Addr |= buf[10];
            Addr <<= 8;
            Addr |= buf[11];
            SetUpgradeFlashSize(remaining, Addr);
        }
        while (erase_offset < write_offset + ret)
        {
            err = esp_partition_erase_range(assert_update_partition, erase_offset, assert_update_partition->erase_size);
            if (err == ESP_OK)
            {
                erase_offset += assert_update_partition->erase_size;
            }
            else
            {
                httpd_resp_send_chunk(req, NULL, 0);
                //ESP_LOGE(TAG, "---- assert 分区擦除失败(%s), 分区容量 = 0x%x, 偏移地址 = 0x%x, 擦除长度 = 0x%x", esp_err_to_name(err), assert_update_partition->size, erase_offset, assert_update_partition->erase_size);
                return ESP_FAIL;
            }
        }

        err = esp_partition_write(assert_update_partition, write_offset, buf, ret);
        if (err == ESP_OK)
        {
            remaining -= ret;
            write_offset += ret;
            recv_block++;
            if ((recv_block % 32) == 0)
            {
                percent = 100.0 - (double)(remaining * 100) / (double)total;
                ESP_LOGI(TAG, "---- 写入数据(%.2f%%), recv_block = %d", percent, recv_block);
            }
        }
        else
        {
            httpd_resp_send_chunk(req, NULL, 0);
            ESP_LOGE(TAG, "---- assert 分区写数据失败(%s)", esp_err_to_name(err));
            return ESP_FAIL;
        }

        // 增加20ms延时, 解决CPU0看门狗超时的问题
        vTaskDelay(pdMS_TO_TICKS(20));

    }
    ESP_LOGI(TAG, "---- write_offset = %d", write_offset);
    if (write_offset == total)
    {
        ESP_LOGI(TAG, "---- assert 分区更新完成, 1秒后自动重启.");
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
    else
    {
        httpd_resp_send_chunk(req, NULL, 0);
        ESP_LOGE(TAG, "---- assert 分区更新数据部分丢失");
        return ESP_FAIL;
    }
    SetUpgradeStart(); // 启动升级
    ESP_LOGI(TAG, "---- 国民技术程序文件: 接收完成");
    return ESP_OK;
    
}


void Nat32G031_ReqReset(void)
{
    ESP_LOGI(TAG, "---- NAT32G031程序更新完成, 3秒后自动重启.");
    // delele_tasks();
    // httpd_resp_sendstr(req, "<p>应用程序更新完成,3秒后自动重启.</p>");
    vTaskDelay(pdMS_TO_TICKS(3000));
    esp_restart();
}

static esp_err_t upload_post_handler(httpd_req_t *req)
{
    const char *uri_post = req->uri;
    ESP_LOGI(TAG, "---- POST URI: 192.168.4.1%s", req->uri);
    if (strcmp(uri_post, "/upload/RT200T-2.bin") == 0)
    {
        return app_post_handler(req);
    }
    // else if (strcmp(uri_post, "/upload/mmap_assert.bin") == 0)
    // {
    //     return assert_post_handler(req);
    // }
    // else if (strcmp(uri_post, "/upload/storage.bin") == 0)
    //{
    //    return storage_post_handler(req);
    //}
    else if (strcmp(uri_post, "/upload/BAT32G139.bin") == 0)
    {
        return nation_post_handler(req);
    }
    else
    {
        ESP_LOGI(TAG, "---- 非预设POST, 不处理, 直接回到起始页");
        return index_html_get_handler(req);
    }

    return ESP_OK;
}

void set_softap_ota_status(unsigned char status)
{
    softap_ota_start = status;
}

unsigned char get_softap_ota_status(void)
{
    return softap_ota_start;
}

httpd_handle_t start_webserver(void)
{
    httpd_handle_t server = NULL;
    httpd_config_t config = HTTPD_DEFAULT_CONFIG();
    config.lru_purge_enable = true;
    config.uri_match_fn = httpd_uri_match_wildcard;

    // Start the httpd server
    ESP_LOGI(TAG, "---- 服务器端口号: '%d'", config.server_port);
    if (httpd_start(&server, &config) == ESP_OK)
    {
        // Set URI handlers
        ESP_LOGI(TAG, "---- 注册URI管理程序");

        // 注册客户端下载行为
        httpd_uri_t download_cfg = {
            .uri = "/*",
            .method = HTTP_GET,
            .handler = download_get_handler,
            .user_ctx = NULL};
        httpd_register_uri_handler(server, &download_cfg);

        // 注册客户端上传行为
        httpd_uri_t upload_cfg = {
            .uri = "/upload/*",
            .method = HTTP_POST,
            .handler = upload_post_handler,
            .user_ctx = NULL};
        httpd_register_uri_handler(server, &upload_cfg);

        return server;
    }

    ESP_LOGI(TAG, "---- 服务器启动时发生错误.");
    return NULL;
}

esp_err_t stop_webserver(httpd_handle_t server)
{
    return httpd_stop(server);
}

/******************************************
 *根据某种状态自动开关webserver的监听处理,
 *没有任何STA连接AP,自动关闭webserver,
 *任意STA连接AP,自动开启webserver.
 ******************************************/
void connect_handler(void *arg)
{
    httpd_handle_t *server = (httpd_handle_t *)arg;
    if (*server == NULL)
    {
        ESP_LOGI(TAG, "---- 开启HTTP服务器");
        *server = start_webserver();
    }
}

void disconnect_handler(void *arg)
{
    httpd_handle_t *server = (httpd_handle_t *)arg;
    if (*server)
    {
        ESP_LOGI(TAG, "---- 关闭HTTP服务器");
        if (stop_webserver(*server) == ESP_OK)
        {
            *server = NULL;
        }
        else
        {
            ESP_LOGE(TAG, "Failed to stop http server");
        }
    }
}

httpd_handle_t http_service(void)
{
    static httpd_handle_t server = NULL;

    // 首次启动http_server
    server = start_webserver();
    if (server)
    {
        ESP_LOGI(TAG, "---- HTTP服务器开启成功.");
    }
    else
    {
        ESP_LOGI(TAG, "---- HTTP服务器开启失败.");
    }

    return server;
}