/*
 * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
 *
 * SPDX-License-Identifier: Unlicense OR CC0-1.0
 */
#include <unistd.h>
#include <stdio.h>
#include "esp_heap_caps.h"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "nvs.h"
#include "bsp_board.h"
#include "bsp_lcd.h"
#include "bsp_btn.h"
#include "bsp_storage.h"
#include "settings.h"
#include "lv_port.h"
#include "app_led.h"
#include "app_rmaker.h"
#include "app_sr.h"
#include "gui/ui_main.h"
#include "pthread.h"
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/wait.h>

//play_sound
#include "app_player.h"
#include "bsp_i2s.h"
#include "bsp_codec.h"
#include "mp3dec.h"
#include "file_manager.h"
#include "driver/i2s.h"
#include "esp_check.h"
#include "freertos/queue.h"
#include <dirent.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <sys/unistd.h>
#include <sys/stat.h>
#include"app_wifi.h"

extern bool s_connected;
void * play_sound(const char *path);
static QueueHandle_t audio_event_queue = NULL;
player_state_t g_player_state;

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

#define AED_PORT 153
#define SERVER_IP "139.9.142.29"
static const char* aedID="1253467986534216";
static const char *TAG = "main";
uint8_t* imageByte;

bool recv_warning_flag=0;

//AED socket 
void * aedSocketMonitorFunc(void* args){
    while(1){
        int emergencyReponseSocket=socket(AF_INET,SOCK_STREAM,0);/*建立socket*/
        if(emergencyReponseSocket==-1){
            printf("socket创建失败");
            sleep(60);
            continue;
        }

        struct sockaddr_in dest_addr;
        //oring:char buf[dataMaxLength];
        dest_addr.sin_family=AF_INET;
        dest_addr.sin_port=htons(AED_PORT);

        if((dest_addr.sin_addr.s_addr=inet_addr(SERVER_IP))==INADDR_NONE) {
            printf("地址无效\n");
            sleep(60);
            continue;
        }
        bzero(&(dest_addr.sin_zero),8);
        if(connect(emergencyReponseSocket,(struct sockaddr*)&dest_addr,sizeof(struct sockaddr))==-1){//连接方法，传入句柄，目标地址和大小
            printf( "服务器连接失败\n");
            recv_warning_flag=0;
            sleep(60);
            continue;
        } else{
            printf("正在注册为AED\n");
            if(send(emergencyReponseSocket,aedID,16,0)==-1) {
                printf("sending failed\n");
                sleep(60);
                continue;
            }
            recv(emergencyReponseSocket,buf,dataMaxLength,0);
            printf("注册成功，等待唤醒\n");
            for(int i=0; i<dataMaxLength;i++)
                buf[i]='\0';
            recv(emergencyReponseSocket,buf,dataMaxLength,0);
            if(strcmp(buf,"abort")==0){
            	printf("服务器关闭，正在重置连接");
            	close(emergencyReponseSocket);
            	continue;
            }
            for(int i=0; i<dataMaxLength;i++)
                infor[i]=buf[i];
            printf("收到报警信息: %s\n",infor);
            send(emergencyReponseSocket,"1",1,0);
            //==========收图===========//
            printf("正在等待现场照片\n");
            char sizeBuff[8]={0};
            char flag[1]={0};
            for(int i=0;i<8;i++) sizeBuff[i]='\0';
            if(recv(emergencyReponseSocket, sizeBuff, 8, 0)<=0){
            	printf("接收失败或者对端关闭连接！\n");
                return 0;
            }
            int size=0;
            for(int i=0;sizeBuff[i]!='\0';i++){
                size*=10;
                size+=sizeBuff[i]-'0';
            }
            printf("接收到尺寸: %d\n",size);
            send(emergencyReponseSocket, flag, 1, 0);
            printf("接收图片字节流 \n");
            //文件接收的
            imageByte=(uint8_t*)malloc(200);
            FILE * image;
            image = fopen("/spiffs/picture.png","w");
            fclose(image);
            int left=size;
            image = fopen("/spiffs/picture.png","wb+");
            while(left>0) 	{
            	printf("剩余%d\n",left);
            	int recvLen=200<left?200:left;
            	left-=recvLen;
                if(0 >= recv(emergencyReponseSocket, imageByte, recvLen, 0)){
                    printf("接收失败或者对端关闭连接！\n");
                    return 0;
                }
                fwrite(imageByte,sizeof(uint8_t),recvLen,image);
                fflush(image);
            }
            fclose(image);
            send(emergencyReponseSocket, flag, 1, 0);
            recv(emergencyReponseSocket, flag, 1, 0);
            //======================
            printf("图片存储完成\n");
            gpio_set_direction(41, GPIO_MODE_OUTPUT);
            gpio_set_level(GPIO_NUM_41, 1);//1为高电平，0为低电平
            recv_warning_flag=1; //to stimulate the text shown in the text box(information)
        }
        close(emergencyReponseSocket);
    }
}

//play_sound
void * play_sound(const char *path)
{
    ESP_LOGI(TAG, "start to decode %s", path);
     /* Audio control event queue */
    audio_event_queue = xQueueCreate(4, sizeof(player_event_t));
    if (NULL == audio_event_queue) {
        vTaskDelete(NULL);
    }
    FILE *fp = NULL;
    int sample_rate = 0;
    uint32_t bits_cfg = 0;
    uint32_t nChans = 0;
    esp_err_t ret = ESP_OK;
    uint8_t *output = NULL;
    uint8_t *read_buf = NULL;
    MP3FrameInfo frame_info;
    HMP3Decoder mp3_decoder = MP3InitDecoder();
    player_event_t audio_event = AUDIO_EVENT_NONE;

    ESP_RETURN_ON_FALSE(NULL != mp3_decoder, ESP_ERR_NO_MEM, TAG, "Failed create MP3 decoder");

    read_buf = malloc(MAINBUF_SIZE);
    ESP_GOTO_ON_FALSE(NULL != read_buf, ESP_ERR_NO_MEM, clean_up, TAG, "Failed allocate read buffer");

    output = malloc(1152 * sizeof(int16_t) * 2);
    ESP_GOTO_ON_FALSE(NULL != output, ESP_ERR_NO_MEM, clean_up, TAG, "Failed allocate output buffer");

    /* Read audio file from given path */
    fp = fopen(path, "rb");
    ESP_GOTO_ON_FALSE(NULL != fp, ESP_ERR_NOT_FOUND, clean_up, TAG, "File \"%s\" does not exist", path);

    typedef struct {
        char header[3];     /*!< Always "ID3" */
        char ver;           /*!< Version, equals to3 if ID3V2.3 */
        char revision;      /*!< Revision, should be 0 */
        char flag;          /*!< Flag byte, use Bit[7..5] only */
        char size[4];       /*!< TAG size */
    } __attribute__((packed)) mp3_id3_header_v2_t;
    /* Get ID3 head */
    mp3_id3_header_v2_t tag;
    if (sizeof(mp3_id3_header_v2_t) == fread(&tag, 1, sizeof(mp3_id3_header_v2_t), fp)) {
        if (memcmp("ID3", (const void *) &tag, sizeof(tag.header)) == 0) {
            int tag_len =
                ((tag.size[0] & 0x7F) << 21) +
                ((tag.size[1] & 0x7F) << 14) +
                ((tag.size[2] & 0x7F) << 7) +
                ((tag.size[3] & 0x7F) << 0);
            fseek(fp, tag_len - sizeof(mp3_id3_header_v2_t), SEEK_SET);
        } else {
            /* Not ID3 header */
            fseek(fp, 0, SEEK_SET);
        }
    }

    /* Start MP3 decoding */
    int bytes_left = 0;
    unsigned char *read_ptr = read_buf;
    do {
        /* Process audio event sent from other task */
        if (pdPASS == xQueueReceive(audio_event_queue, &audio_event, 0)) {
            if (AUDIO_EVENT_PAUSE == audio_event) {
                g_player_state = PLAYER_STATE_PAUSE;
                i2s_zero_dma_buffer(I2S_NUM_0);
                xQueuePeek(audio_event_queue, &audio_event, portMAX_DELAY);
                continue;
            }

            if (AUDIO_EVENT_CHANGE == audio_event ||
                    AUDIO_EVENT_NEXT == audio_event ||
                    AUDIO_EVENT_PREV == audio_event) {
                i2s_zero_dma_buffer(I2S_NUM_0);
                ret = ESP_FAIL;
                goto clean_up;
            }
        }
        g_player_state = PLAYER_STATE_PLAYING;

        /* Read `mainDataBegin` size to RAM */
        if (bytes_left < MAINBUF_SIZE) {
            memmove(read_buf, read_ptr, bytes_left);
            size_t bytes_read = fread(read_buf + bytes_left, 1, MAINBUF_SIZE - bytes_left, fp);
            ESP_GOTO_ON_FALSE(bytes_read > 0, ESP_OK, clean_up, TAG, "No data read from strorage device");
            bytes_left = bytes_left + bytes_read;
            read_ptr = read_buf;
        }

        /* Find MP3 sync word from read buffer */
        int offset = MP3FindSyncWord(read_buf, MAINBUF_SIZE);

        if (offset >= 0) {
            read_ptr += offset;         /*!< Data start point */
            bytes_left -= offset;       /*!< In buffer */
            int mp3_dec_err = MP3Decode(mp3_decoder, &read_ptr, &bytes_left, (int16_t *) output, 0);
            ESP_GOTO_ON_FALSE(ERR_MP3_NONE == mp3_dec_err, ESP_FAIL, clean_up, TAG, "Can't decode MP3 frame");

            /* Get MP3 frame info and configure I2S clock */
            MP3GetLastFrameInfo(mp3_decoder, &frame_info);

            /* Configure I2S clock if sample rate changed. Always reconfigure at first frame */
            if (sample_rate != frame_info.samprate ||
                    nChans != frame_info.nChans ||
                    bits_cfg != frame_info.bitsPerSample) {
                sample_rate = frame_info.samprate;
                bits_cfg = frame_info.bitsPerSample;
                nChans = frame_info.nChans;
                ESP_LOGI(TAG, "audio info: sr=%d, bit=%d, ch=%d", sample_rate, bits_cfg, nChans);
                i2s_channel_t channel = (nChans == 1) ? I2S_CHANNEL_MONO : I2S_CHANNEL_STEREO;
                //i2s_channel_t channel = I2S_CHANNEL_MONO ;
                i2s_set_clk(I2S_NUM_0, sample_rate, bits_cfg, channel);
            }

            /* Write decoded data to audio decoder */
            size_t i2s_bytes_written = 0;
            size_t output_size = frame_info.outputSamps * nChans;
            i2s_write(I2S_NUM_0, output, output_size, &i2s_bytes_written, portMAX_DELAY);
            ESP_LOGI(TAG,"runing to here");
        } else {
            /* Sync word not found in frame. Try to read next frame */
            ESP_LOGE(TAG, "MP3 sync word not found");
            bytes_left = 0;
            continue;
        }
    } while (true);

clean_up:
    /* This will prevent from sending dumy data to audio decoder */
    i2s_zero_dma_buffer(I2S_NUM_0);

    /* Clean up resources */
    if (NULL != mp3_decoder) {
        MP3FreeDecoder(mp3_decoder);
    }
    if (NULL != fp) {
        fclose(fp);
    }
    if (NULL != read_buf) {
        free(read_buf);
    }
    if (NULL != output) {
        free(output);
    }

    return ret;
}



#define MEMORY_MONITOR 0

#if MEMORY_MONITOR
static void monitor_task(void *arg)
{
    (void) arg;
    const int STATS_TICKS = pdMS_TO_TICKS(2 * 1000);

    while (true) {
        ESP_LOGI(TAG, "System Info Trace");
        printf("\tDescription\tInternal\tSPIRAM\n");
        printf("Current Free Memory\t%d\t\t%d\n",
               heap_caps_get_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL),
               heap_caps_get_free_size(MALLOC_CAP_SPIRAM));
        printf("Largest Free Block\t%d\t\t%d\n",
               heap_caps_get_largest_free_block(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL),
               heap_caps_get_largest_free_block(MALLOC_CAP_SPIRAM));
        printf("Min. Ever Free Size\t%d\t\t%d\n",
               heap_caps_get_minimum_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL),
               heap_caps_get_minimum_free_size(MALLOC_CAP_SPIRAM));

        vTaskDelay(STATS_TICKS);
    }

    vTaskDelete(NULL);
}

static void sys_monitor_start(void)
{
    BaseType_t ret_val = xTaskCreatePinnedToCore(monitor_task, "Monitor Task", 4 * 1024, NULL, configMAX_PRIORITIES - 3, NULL, 0);
    ESP_ERROR_CHECK_WITHOUT_ABORT((pdPASS == ret_val) ? ESP_OK : ESP_FAIL);
}
#endif

void app_main(void)
{
    ESP_LOGI(TAG, "Compile time: %s %s", __DATE__, __TIME__);
    /* Initialize NVS. */
    esp_err_t err = nvs_flash_init();
    if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) {
        ESP_ERROR_CHECK(nvs_flash_erase());
        err = nvs_flash_init();
    }
    ESP_ERROR_CHECK(err);
    ESP_ERROR_CHECK(settings_read_parameter_from_nvs());
#if !SR_RUN_TEST && MEMORY_MONITOR
    sys_monitor_start(); // Logs should be reduced during SR testing
#endif
    ESP_ERROR_CHECK(bsp_board_init());
    ESP_ERROR_CHECK(bsp_board_power_ctrl(POWER_MODULE_AUDIO, true));
    ESP_ERROR_CHECK(lv_port_init());
    ESP_ERROR_CHECK(bsp_spiffs_init("model", "/srmodel", 4));
    ESP_ERROR_CHECK(bsp_spiffs_init("storage", "/spiffs", 2));
    ESP_ERROR_CHECK(ui_main_start());
    bsp_lcd_set_backlight(true);  // Turn on the backlight after gui initialize
    ESP_ERROR_CHECK(app_player_start("/spiffs/mp3"));

    const board_res_desc_t *brd = bsp_board_get_description();
    app_pwm_led_init(brd->PMOD2->row1[1], brd->PMOD2->row1[2], brd->PMOD2->row1[3]);
    ESP_LOGI(TAG, "speech recognition start");
    app_sr_start(false);
    app_rmaker_start();
    
    while(!s_connected){sleep(5);};
    pthread_t aedMontorThread;
    pthread_create(&aedMontorThread,NULL,aedSocketMonitorFunc,NULL);
    pthread_join(aedMontorThread,NULL);
}
