// #include <stdio.h>
// #include "freertos/FreeRTOS.h"
// #include "freertos/task.h"


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

//WiFi Soft-Access Point
#include "esp_wifi.h"
#include "esp_event_loop.h"
#include "nvs_flash.h"


#define DEVICE_SSID      "auxbridge"
#define DEVICE_PASS      ""

//HTTP Servers
#include "tcpip_adapter.h"


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

//SD Card
#include <sys/unistd.h>
#include <sys/stat.h>
#include "esp_vfs_fat.h"
#include "driver/sdmmc_host.h"
#include "sdmmc_cmd.h"

//i2s
#include <stdlib.h>
#include <stddef.h>
#include <inttypes.h>
#include <stdbool.h>
#include <sys/time.h>

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "driver/i2s.h"
#include "driver/adc.h"


// fix for i2s error with SPH54065 MEMS microphone
 #include "soc/i2s_reg.h"

//file server
#include "esp_http_server.h"
#include "esp_vfs.h"

//file info
#include <dirent.h> 
#include <sys/stat.h>
#include <time.h>

//GPIO
#include "driver/gpio.h"



/* Max length a file path can have on storage */
#define FILE_PATH_MAX (ESP_VFS_PATH_MAX + CONFIG_SPIFFS_OBJ_NAME_LEN)

/* Max size of an individual file. Make sure this
 * value is same as that set in upload_script.html */
#define MAX_FILE_SIZE   (200*1024) // 200 KB
#define MAX_FILE_SIZE_STR "200KB"

/* Scratch buffer size */
#define SCRATCH_BUFSIZE  8192

struct file_server_data {
    /* Base path of file storage */
    char base_path[ESP_VFS_PATH_MAX + 1];

    /* Scratch buffer for temporary storage during file transfer */
    char scratch[SCRATCH_BUFSIZE];
};


static const char *TAG = "auxbridge";

  ///////////////////////////////////////////////
 //// SD CARD //////////////////////////////////
///////////////////////////////////////////////

void sd_init(void)
{
    ESP_LOGI(TAG, "Initializing SD card");
    ESP_LOGI(TAG, "Using SDMMC peripheral");
    sdmmc_host_t host = SDMMC_HOST_DEFAULT();
    sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
    gpio_set_pull_mode(15, GPIO_PULLUP_ONLY);   // CMD, needed in 4- and 1- line modes
    gpio_set_pull_mode(2, GPIO_PULLUP_ONLY);    // D0, needed in 4- and 1-line modes
    gpio_set_pull_mode(4, GPIO_PULLUP_ONLY);    // D1, needed in 4-line mode only
    gpio_set_pull_mode(12, GPIO_PULLUP_ONLY);   // D2, needed in 4-line mode only
    gpio_set_pull_mode(13, GPIO_PULLUP_ONLY);   // D3, needed in 4- and 1-line modes

    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;
    esp_err_t ret = esp_vfs_fat_sdmmc_mount("/sd", &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 format_if_mount_failed = true.");
        } 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;
    }

    sdmmc_card_print_info(stdout, card);
}

void sd_test() {
    ESP_LOGI(TAG, "Opening file");
    FILE* f = fopen("/sd/hello.txt", "w");
    if (f == NULL) {
        ESP_LOGE(TAG, "Failed to open file for writing");
        return;
    }

    fprintf(f, "Hello there!\n");
    fclose(f);
}


void gpio_init() {
    gpio_pad_select_gpio(GPIO_NUM_16);
    gpio_set_direction(GPIO_NUM_16, GPIO_MODE_OUTPUT); 
    gpio_set_level(GPIO_NUM_16, 1);


}

#define I2S_SAMPLE_RATE   (44100)
#define I2S_NUM (I2S_NUM_0)
#define I2S_READ_LEN      (16 * 1024) // bits per sample * dma_buf_len

//http://www.topherlee.com/software/pcm-tut-wavformat.html

void wav_header(char* header, int waveFileSize){
  header[0] = 'R';
  header[1] = 'I';
  header[2] = 'F';
  header[3] = 'F';
  unsigned int fileSizeMinus8 = waveFileSize - 8;
  header[4] = (char)(fileSizeMinus8 & 0xFF);
  header[5] = (char)((fileSizeMinus8 >> 8) & 0xFF);
  header[6] = (char)((fileSizeMinus8 >> 16) & 0xFF);
  header[7] = (char)((fileSizeMinus8 >> 24) & 0xFF);
  header[8] = 'W';
  header[9] = 'A';
  header[10] = 'V';
  header[11] = 'E';
  header[12] = 'f';
  header[13] = 'm';
  header[14] = 't';
  header[15] = ' ';
  header[16] = 0x10;  // linear PCM
  header[17] = 0x00;
  header[18] = 0x00;
  header[19] = 0x00;
  header[20] = 0x01;  // linear PCM
  header[21] = 0x00;
  header[22] = 0x01;  // monoral
  header[23] = 0x00;
  header[24] = 0x44;  // sampling rate 44100
  header[25] = 0xAC;
  header[26] = 0x00;
  header[27] = 0x00;
  header[28] = 0x88;  // Byte/sec = 44100x (16 / 8) x1 = 88200
  header[29] = 0x58;
  header[30] = 0x01;
  header[31] = 0x00;
  header[32] = 0x02;  // 16bit monoral
  header[33] = 0x00;
  header[34] = 0x10;  // 16bit
  header[35] = 0x00;
  header[36] = 'd';
  header[37] = 'a';
  header[38] = 't';
  header[39] = 'a';
  unsigned int fileSizeMinusHeader = waveFileSize - 8;
  header[40] = (char)(fileSizeMinusHeader & 0xFF);
  header[41] = (char)((fileSizeMinusHeader >> 8) & 0xFF);
  header[42] = (char)((fileSizeMinusHeader >> 16) & 0xFF);
  header[43] = (char)((fileSizeMinusHeader >> 24) & 0xFF);
}

void i2s_init(void)
{
     i2s_config_t i2s_config = {
        .mode = I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN | I2S_MODE_ADC_BUILT_IN,
        .sample_rate =  I2S_SAMPLE_RATE,
        .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
        .communication_format = I2S_COMM_FORMAT_I2S_MSB, // Big Endian
        .channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT,
        .intr_alloc_flags = 0,
        .dma_buf_count = 5,
        .dma_buf_len = 1024,
        .use_apll = 1,
     };
     //install and start i2s driver
     i2s_driver_install(I2S_NUM, &i2s_config, 0, NULL);
     //init ADC pad
     i2s_set_adc_mode(ADC_UNIT_1, ADC1_CHANNEL_7);
}

void disp_buf(uint8_t* buf, int length)
{
    printf("======\n");
    for (int i = 0; i < length; i++) {
        printf("%02x ", buf[i]);
        if ((i + 1) % 16 == 0) {
            printf("\n");
        }
    }
    printf("======\n");
}

void i2s_sample() {
    int i2s_read_len = I2S_READ_LEN;
    size_t bytes_read, bytes_written;

    char* i2s_read_buff = (char*) calloc(i2s_read_len, sizeof(char));
    uint8_t* flash_write_buff = (uint8_t*) calloc(i2s_read_len, sizeof(char));
    i2s_adc_enable(I2S_NUM);

    int limit = 700;

   printf("Create file: sample.txt\n");

    FILE* f = fopen("/sd/sample.txt", "w");
    // char* header = calloc(44, sizeof(char));
    // wav_header(header, 0);
    // fwrite(header, 1, 44, f);
    // if (f == NULL) {
    //     ESP_LOGE(TAG, "Failed to open file for writing");
    //     return;
    // }

    while (1) {
        i2s_read(I2S_NUM, (void*) i2s_read_buff, i2s_read_len, &bytes_read, portMAX_DELAY);
        disp_buf((uint8_t*) i2s_read_buff, 64);

        if (limit > 0) {
            printf("%i Write to file: sample.wav\n", limit);
            fwrite(i2s_read_buff, 16, 1024, f);
            limit--;
        } else if (limit == 0) {
            fclose(f);

            struct stat file_stat;
            stat("/sd/sample.txt", &file_stat);

            // update WAV header with file size
            // FILE* f = fopen("/sd/sample.wav", "r+b");
            // wav_header(header, file_stat.st_size);
            // fseek(f, 0, SEEK_SET);
            // fwrite(header, 1, 44, f);
            // fclose(f);


            printf("Done writing sample file (%ld bytes).\n\n", file_stat.st_size);
            limit--;

        } 
    }

}

// const int headerSize = 44;
// const int waveDataSize = record_time * 88000;
// const int numCommunicationData = 8000;
// const int numPartWavData = numCommunicationData/4;
// byte header[headerSize];
// char communicationData[numCommunicationData];
// char partWavData[numPartWavData];

// #define SAMPLE_RATE     (48000) 
// #define I2S_NUM         (0)
// #define BUFFER_SAMPLES  (2048)
// #define BITS_PER_SAMPLE (32)
// #define WS (GPIO_NUM_27) 
// #define BLK (GPIO_NUM_25) 
// #define DATA (GPIO_NUM_26) 

// static void init_i2s()
// {
//     const int sample_rate = 22050;//44100;

//     gpio_set_direction(WS, GPIO_MODE_OUTPUT); 
//     gpio_set_direction(BLK, GPIO_MODE_OUTPUT); 
//     gpio_set_direction(DATA, GPIO_MODE_INPUT); 

//     /* RX: I2S_NUM_0 */
//     i2s_config_t i2s_config_rx = {
//     .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX),
//     .sample_rate = sample_rate,
//     .bits_per_sample = I2S_BITS_PER_SAMPLE_32BIT,
//     .channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT,   
//     .communication_format = (i2s_comm_format_t)(I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB),
//     .intr_alloc_flags = 0,
//     .dma_buf_count = 32,                            // number of buffers, 128 max.
//     .dma_buf_len = 512                         // size of each buffer
//     };

//     i2s_pin_config_t pin_config_rx = {
//         .bck_io_num = BLK,
//         .ws_io_num = WS,
//         .data_out_num = I2S_PIN_NO_CHANGE,
//         .data_in_num =  DATA
//     };

//     i2s_driver_install(I2S_NUM_0, &i2s_config_rx, 0, NULL);
//     // REG_SET_BIT(  I2S_TIMING_REG(I2S_NUM_0),BIT(9));    
//     // REG_SET_BIT( I2S_CONF_REG(I2S_NUM_0), I2S_RX_MSB_SHIFT);
//     i2s_set_pin(I2S_NUM_0, &pin_config_rx);
//     // i2s_mclk_gpio_select(I2S_NUM_0, 27);

//      // i2s_set_clk(I2S_NUM_0, sample_rate, I2S_BITS_PER_SAMPLE_32BIT, I2S_CHANNEL_FMT_ONLY_RIGHT);
//     //i2s_set_sample_rates(I2S_NUM_0, 16000); //set sample rates




// }



// int i2s_Read(char* data, int numData) {
//   return i2s_read_bytes(I2S_NUM_0, (char *)data, numData, portMAX_DELAY);
// }




// void task_megaphone(void *pvParams)
// {
//     int buf_len = 1024;
//     char* buf = (char*) calloc(buf_len, sizeof(char));


//     // struct timeval tv = {0};
//     // struct timezone *tz = {0};
//     // gettimeofday(&tv, &tz);
//     // uint64_t micros = tv.tv_usec + tv.tv_sec * 1000000;
//     // uint64_t micros_prev = micros;
//     // uint64_t delta = 0;

//     init_i2s();


//     while(1)
//     {
//         size_t num_bytes_read = 0;
//         //char *buf_ptr_write = buf;

//         // read whole block of samples
//         esp_err_t ret = i2s_read(I2S_NUM_0, (void*) buf, buf_len, &num_bytes_read, portMAX_DELAY);

//          if (ret != ESP_OK) {
//             ESP_LOGE(TAG, "Parameter Error with i2s_read"); 
//         }
//         printf("num bytes: %d\n", num_bytes_read);

//         example_disp_buf((uint8_t*)(uint8_t*) buf, 64);

//         // uint32_t samples_read = num_bytes_read / 2 / (I2S_BITS_PER_SAMPLE_32BIT / 8);

//         // printf("%s samples\n", num_bytes_read);

//         //  convert 2x 32 bit stereo -> 1 x 16 bit mono
//         for(int i = 0; i < 32; i++) {
//              printf("%d ", buf[i]);
//         }
//         printf("...\nok.");
//         // local echo
//         // bytes_written = samples_read * 2 * (I2S_BITS_PER_SAMPLE_16BIT / 8);
//         // i2s_write_bytes(I2S_NUM_0, buf, bytes_written, portMAX_DELAY);

//         // cnt += samples_read;

//         // if(cnt >= 22500) {
//         //     gettimeofday(&tv, &tz);
//         //     micros = tv.tv_usec + tv.tv_sec * 1000000;
//         //     delta = micros - micros_prev;
//         //     micros_prev = micros;
//         //     printf("%d samples in %" PRIu64 " usecs\n", cnt, delta);

//         //     cnt = 0;
//         // }
//     }
// }

/**
 * entry point
 */


#define IS_FILE_EXT(filename, ext) \
    (strcasecmp(&filename[strlen(filename) - sizeof(ext) + 1], ext) == 0)

/* Set HTTP response content type according to file extension */
static esp_err_t set_content_type_from_file(httpd_req_t *req, const char *filename)
{
    if (IS_FILE_EXT(filename, ".pdf")) {
        return httpd_resp_set_type(req, "application/pdf");
    } else if (IS_FILE_EXT(filename, ".html")) {
        return httpd_resp_set_type(req, "text/html");
    } else if (IS_FILE_EXT(filename, ".jpeg")) {
        return httpd_resp_set_type(req, "image/jpeg");
    } else if (IS_FILE_EXT(filename, ".ico")) {
        return httpd_resp_set_type(req, "image/x-icon");
    }
    /* This is a limited set only */
    /* For any other type always set as plain text */
    return httpd_resp_set_type(req, "text/plain");
}

static const char* get_path_from_uri(char *dest, const char *base_path, char *uri, size_t destsize)
{
    // const size_t base_pathlen = strlen(base_path);
    // size_t pathlen = strlen(uri);


    // if (base_pathlen + pathlen + 1 > destsize) {
    //     /* Full path string won't fit into destination buffer */
    //     return NULL;
    // }

    // char *out = basename(uri);
    // int length = strlen(out);
    // printf("%s\n", out);
    // /* Construct full path (base + path) */
    // strcpy(dest, base_path);
    // strlcpy(dest + base_pathlen, basename(uri), length + 1);


    // // char *idx = strchr(uri, '/');
    // // while (idx != NULL) {
    // //     if (uri[idx - uri] == 'f' &&
    // //         uri[idx - uri + 1] == 'i' &&
    // //         uri[idx - uri + 2] == 'l' &&
    // //         uri[idx - uri + 3] == 'e' &&
    // //         uri[idx - uri + 4] == '/') {
    // //         sprintf(dest, "%s%.*s", base_path, pathlen - (idx - uri + 5), uri + idx);
    // //         return dest;
    // //     }

    // //     idx = strchr(uri, '/');
    // // }

    // return dest;

    // // //https://stackoverflow.com/questions/4214314/get-a-substring-of-a-char
    // // /* Construct full path (base + path) */
    // // sprintf(dest, "%s%.*s", base_path, )
    // // strcpy(dest, base_path);
    // // strlcpy(dest + base_pathlen, uri, pathlen + 1);

    // // /* Return pointer to path, skipping the base */
    return dest;
}



/* Handler to download a file kept on the server */
static esp_err_t download_get_handler(httpd_req_t *req)
{
    char filepath[FILE_PATH_MAX];
    FILE *fd = NULL;
    struct stat file_stat;

    const char *filename = get_path_from_uri(filepath, ((struct file_server_data *)req->user_ctx)->base_path,
                                             req->uri, sizeof(filepath));
    if (!filename) {
        ESP_LOGE(TAG, "Filename is too long");
        /* Respond with 500 Internal Server Error */
        httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Filename too long");
        return ESP_FAIL;
    }


    if (stat(filepath, &file_stat) == -1) {
        
        ESP_LOGE(TAG, "Failed to stat file : %s", filepath);
        /* Respond with 404 Not Found */
        httpd_resp_send_err(req, HTTPD_404_NOT_FOUND, "File does not exist");
        return ESP_FAIL;
    }

    fd = fopen(filepath, "r");
    if (!fd) {
        ESP_LOGE(TAG, "Failed to read existing file : %s", filepath);
        /* Respond with 500 Internal Server Error */
        httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to read existing file");
        return ESP_FAIL;
    }

    ESP_LOGI(TAG, "Sending file : %s (%ld bytes)...", filename, file_stat.st_size);
    set_content_type_from_file(req, filename);

    /* Retrieve the pointer to scratch buffer for temporary storage */
    char *chunk = ((struct file_server_data *)req->user_ctx)->scratch;
    size_t chunksize;
    do {
        /* Read file in chunks into the scratch buffer */
        chunksize = fread(chunk, 1, SCRATCH_BUFSIZE, fd);

        /* Send the buffer contents as HTTP response chunk */
        if (httpd_resp_send_chunk(req, chunk, chunksize) != ESP_OK) {
            fclose(fd);
            ESP_LOGE(TAG, "File sending failed!");
            /* Abort sending file */
            httpd_resp_sendstr_chunk(req, NULL);
            /* Respond with 500 Internal Server Error */
            httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to send file");
            return ESP_FAIL;
        }

        /* Keep looping till the whole file is sent */
    } while (chunksize != 0);

    /* Close file after sending complete */
    fclose(fd);
    ESP_LOGI(TAG, "File sending complete");

    /* Respond with an empty chunk to signal HTTP response completion */
    httpd_resp_send_chunk(req, NULL, 0);
    return ESP_OK;
}

static esp_err_t sample_get_handler(httpd_req_t *req)
{
    ESP_LOGI(TAG, "Reading sample file");

    char filepath[FILE_PATH_MAX];
    FILE *fd = NULL;
    struct stat file_stat;

    fd = fopen("/sd/sample.wav", "r");
    if (!fd) {
        ESP_LOGE(TAG, "Failed to read existing file : %s", filepath);
        /* Respond with 500 Internal Server Error */
        httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to read existing file");
        return ESP_FAIL;
    }

    ESP_LOGI(TAG, "Sending file : sample.txt...");
    set_content_type_from_file(req, "sample.txt");

    /* Retrieve the pointer to scratch buffer for temporary storage */
    char *chunk = ((struct file_server_data *)req->user_ctx)->scratch;
    size_t chunksize;
    do {
        /* Read file in chunks into the scratch buffer */
        chunksize = fread(chunk, 1, SCRATCH_BUFSIZE, fd);

        /* Send the buffer contents as HTTP response chunk */
        if (httpd_resp_send_chunk(req, chunk, chunksize) != ESP_OK) {
            fclose(fd);
            ESP_LOGE(TAG, "File sending failed!");
            /* Abort sending file */
            httpd_resp_sendstr_chunk(req, NULL);
            /* Respond with 500 Internal Server Error */
            httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to send file");
            return ESP_FAIL;
        }

        /* Keep looping till the whole file is sent */
    } while (chunksize != 0);

    /* Close file after sending complete */
    fclose(fd);
    ESP_LOGI(TAG, "File sending complete");

    /* Respond with an empty chunk to signal HTTP response completion */
    httpd_resp_send_chunk(req, NULL, 0);
    return ESP_OK;
}

#define JSON_NO_DELIMITER 0
#define JSON_WITH_DELIMITER 1
#define JSON_STRING_FORMAT "\"%s\":\"%s\"%s"
#define JSON_NUM_FORMAT "\"%s\":%s%s"
#define JSON_FILESIZE_FORMAT "\"%s\":%ld%s"

#define JSON_STRING_FORMAT_WITH_DELIMITER JSON_STRING_FORMAT, JSON_WITH_DELIMITER
#define JSON_STRING_FORMAT_NO_DELIMITER JSON_STRING_FORMAT, JSON_NO_DELIMITER
#define JSON_NUM_FORMAT_WITH_DELIMITER JSON_NUM_FORMAT, JSON_WITH_DELIMITER
#define JSON_NUM_FORMAT_NO_DELIMITER JSON_NUM_FORMAT, JSON_NO_DELIMITER

char* json(char *ret, char *key, char *val, const char *fmt, int deliminator) {
    sprintf(ret, fmt, key, val, (deliminator == JSON_NO_DELIMITER) ? "\n": ",\n");
    return ret;
} 


static esp_err_t device_status_handler(httpd_req_t *req)
{
    char buf[80];

    httpd_resp_set_type(req, "application/json");

    httpd_resp_sendstr_chunk(req, "{\n");

    httpd_resp_sendstr_chunk(req, json(buf, "name", "auxbridge", JSON_STRING_FORMAT_WITH_DELIMITER));
    httpd_resp_sendstr_chunk(req, json(buf, "author", "mschrage", JSON_STRING_FORMAT_WITH_DELIMITER));
    httpd_resp_sendstr_chunk(req, json(buf, "version", "1.0", JSON_NUM_FORMAT_NO_DELIMITER));

    httpd_resp_sendstr_chunk(req, "}");

    httpd_resp_send_chunk(req, NULL, 0);
    return ESP_OK;
}

static esp_err_t manifest_handler(httpd_req_t *req) {
    char buf[80];

    char entrypath[20];

    struct dirent *entry;
    struct stat entry_stat;
    char entrysize[16];

    char *dirpath = "/sd/";
    DIR *dir = opendir(dirpath);
    const size_t dirpath_len = strlen(dirpath);
    strlcpy(entrypath, dirpath, sizeof(entrypath));

    httpd_resp_set_type(req, "application/json");

    httpd_resp_sendstr_chunk(req, "{\n");

    httpd_resp_sendstr_chunk(req, "\"files\": [\n");

    int count = 0;

    while ((entry = readdir(dir)) != NULL) {

        strlcpy(entrypath + dirpath_len, entry->d_name, sizeof(entrypath) - dirpath_len);
        if (stat(entrypath, &entry_stat) == -1) {
            ESP_LOGE(TAG, "Failed to stat %s, %s", entrypath, entry->d_name);
            continue;
        }

        sprintf(entrysize, "%ld", entry_stat.st_size);
        ESP_LOGI(TAG, "Found %s (%s bytes)", entry->d_name, entrysize);

        if (count > 0) {
            httpd_resp_sendstr_chunk(req, ",");
        }

        httpd_resp_sendstr_chunk(req, "{\n");
        httpd_resp_sendstr_chunk(req, json(buf, "id", entry->d_name, JSON_STRING_FORMAT_WITH_DELIMITER));
        //httpd_resp_sendstr_chunk(req, json(buf, "dt", ctime(&entry_stat.st_mtime), JSON_STRING_FORMAT_WITH_DELIMITER));
        httpd_resp_sendstr_chunk(req, json(buf, "size", entrysize, JSON_NUM_FORMAT_WITH_DELIMITER));


        httpd_resp_sendstr_chunk(req, "}");
        count++;

    }
    closedir(dir);
    httpd_resp_sendstr_chunk(req, "]\n");


    httpd_resp_sendstr_chunk(req, "}");

    httpd_resp_send_chunk(req, NULL, 0);
    return ESP_OK;
}

esp_err_t server_init(const char *base_path) {
 static struct file_server_data *server_data = NULL;

    /* Validate file storage base path */
    if (!base_path || strcmp(base_path, "/sd") != 0) {
        ESP_LOGE(TAG, "File server presently supports only '/sd' as base path");
        return ESP_ERR_INVALID_ARG;
    }

    if (server_data) {
        ESP_LOGE(TAG, "File server already started");
        return ESP_ERR_INVALID_STATE;
    }

    /* Allocate memory for server data */
    server_data = calloc(1, sizeof(struct file_server_data));
    if (!server_data) {
        ESP_LOGE(TAG, "Failed to allocate memory for server data");
        return ESP_ERR_NO_MEM;
    }
    strlcpy(server_data->base_path, base_path,
            sizeof(server_data->base_path));

    httpd_handle_t server = NULL;
    httpd_config_t config = HTTPD_DEFAULT_CONFIG();

    /* Use the URI wildcard matching function in order to
     * allow the same handler to respond to multiple different
     * target URIs which match the wildcard scheme */
    config.uri_match_fn = httpd_uri_match_wildcard;

    ESP_LOGI(TAG, "Starting server on port: '%d'", config.server_port);
    if (httpd_start(&server, &config) != ESP_OK) {
        ESP_LOGE(TAG, "Error starting file server!");
        return ESP_FAIL;
    }

    ESP_LOGI(TAG, "Registering URI handlers");

    httpd_uri_t sample_download = {
        .uri       = "/sample", 
        .method    = HTTP_GET,
        .handler   = sample_get_handler,
        .user_ctx  = server_data    // Pass server data as context
    };
    httpd_register_uri_handler(server, &sample_download);

    /* URI handler for getting uploaded files */
    httpd_uri_t file_download = {
        .uri       = "/file/*",  // Match all URIs of type /path/to/file
        .method    = HTTP_GET,
        .handler   = download_get_handler,
        .user_ctx  = server_data    // Pass server data as context
    };
    httpd_register_uri_handler(server, &file_download);

    httpd_uri_t device_status = {
        .uri       = "/device",   // Match all URIs of type /upload/path/to/file
        .method    = HTTP_GET,
        .handler   = device_status_handler,
        .user_ctx  = server_data    // Pass server data as context
    };
    httpd_register_uri_handler(server, &device_status);

    /* URI handler for deleting files from server */
    httpd_uri_t manifest = {
        .uri       = "/manifest",   // Match all URIs of type /delete/path/to/file
        .method    = HTTP_GET,
        .handler   = manifest_handler,
        .user_ctx  = server_data    // Pass server data as context
    };
    httpd_register_uri_handler(server, &manifest);

    return ESP_OK;
}

/* FreeRTOS event group to signal when we are connected*/
static EventGroupHandle_t s_wifi_event_group;

static esp_err_t event_handler(void *ctx, system_event_t *event)
{
    switch(event->event_id) {
    case SYSTEM_EVENT_AP_STACONNECTED:
        ESP_LOGI(TAG, "station:"MACSTR" join, AID=%d",
                 MAC2STR(event->event_info.sta_connected.mac),
                 event->event_info.sta_connected.aid);
        break;
    case SYSTEM_EVENT_AP_STADISCONNECTED:
        ESP_LOGI(TAG, "station:"MACSTR"leave, AID=%d",
                 MAC2STR(event->event_info.sta_disconnected.mac),
                 event->event_info.sta_disconnected.aid);
        break;
    default:
        break;
    }
    return ESP_OK;
}

void wifi_init(void)
{
     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);

    s_wifi_event_group = xEventGroupCreate();

    tcpip_adapter_init();
    ESP_ERROR_CHECK(esp_event_loop_init(event_handler, NULL));

    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));
    wifi_config_t wifi_config = {
        .ap = {
            .ssid = DEVICE_SSID,
            .ssid_len = strlen(DEVICE_SSID),
            .password = DEVICE_PASS,
            .max_connection = 1,
            .authmode = WIFI_AUTH_OPEN
        },
    };

    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP));
    ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_AP, &wifi_config));
    ESP_ERROR_CHECK(esp_wifi_start());

            tcpip_adapter_ip_info_t ip_info;
        IP4_ADDR(&ip_info.ip, 192, 168, 1, 1);
        IP4_ADDR(&ip_info.gw, 192, 168, 1, 1);
        IP4_ADDR(&ip_info.netmask, 255, 255, 255, 0);
        //192.168.4.1

        // tcpip_adapter_ap_start((uint8_t *)"9C:B6:D0:E7:30:0F", &ip_info);

    ESP_LOGI(TAG, "wifi_init_softap finished.SSID:%s password:%s",
             DEVICE_SSID, DEVICE_PASS);

    // start_webserver();
}

void app_main()
{
    printf("starting app_main()\n");
    gpio_init();
    sd_init();
    sd_test();
    wifi_init();
    server_init("/sd");
    i2s_init();
    i2s_sample();

}

// s