/* Play MP3 file from Flash(spiffs system)

   This example code is in the Public Domain (or CC0 licensed, at your option.)

   Unless required by applicable law or agreed to in writing, this
   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied.
*/
#include <string.h>

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

#include "esp_log.h"
#include "sdkconfig.h"

#include "audio_element.h"
#include "audio_pipeline.h"
#include "audio_event_iface.h"
#include "i2s_stream.h"
#include "spiffs_stream.h"
#include "mp3_decoder.h"

#include "esp_peripherals.h"
#include "periph_spiffs.h"
#include "board.h"

static const char *TAG = "SPIFFS_MP3";

void envelope_volume_to(double volume);
audio_element_handle_t create_envelope_filter();


static audio_event_iface_handle_t evt;
static audio_pipeline_handle_t pipeline;
static audio_element_handle_t spiffs_stream_reader, i2s_stream_writer, mp3_decoder;
static audio_element_handle_t volume_filter;
static esp_periph_set_handle_t set;

void play_spiffs_initialize()
{
     int64_t initialization_start = esp_timer_get_time();

    // esp_log_level_set("AUDIO_ELEMENT", ESP_LOG_NONE);
     esp_log_level_set(TAG, ESP_LOG_ERROR);
    // esp_log_level_set("PERIPH_SPIFFS", ESP_LOG_ERROR);
    // esp_log_level_set("new_codec", ESP_LOG_ERROR);
    // esp_log_level_set("AUDIO_HAL", ESP_LOG_ERROR);
    // esp_log_level_set("I2S", ESP_LOG_ERROR);
    // esp_log_level_set("AUDIO_PIPELINE", ESP_LOG_ERROR);
    // esp_log_level_set("MP3_DECODER", ESP_LOG_ERROR);
    // esp_log_level_set("I2S_STREAM", ESP_LOG_ERROR);
    // esp_log_level_set("ADF_BIT_STREAM", ESP_LOG_ERROR);
    // esp_log_level_set("SPIFFS_STREAM", ESP_LOG_ERROR);
    // esp_log_level_set("MY_BOARD_V1_0", ESP_LOG_ERROR);
    // esp_log_level_set("AUDIO_BOARD", ESP_LOG_ERROR);
    

    // Initialize peripherals management
    esp_periph_config_t periph_cfg = DEFAULT_ESP_PERIPH_SET_CONFIG();
    set = esp_periph_set_init(&periph_cfg);

    ESP_LOGI(TAG, "[ 1 ] Mount spiffs");
    // Initialize Spiffs peripheral
    periph_spiffs_cfg_t spiffs_cfg = {
        .root = "/spiffs",
        .partition_label = NULL,
        .max_files = 5,
        .format_if_mount_failed = true};
    esp_periph_handle_t spiffs_handle = periph_spiffs_init(&spiffs_cfg);

    // Start spiffs
    esp_periph_start(set, spiffs_handle);

    // Wait until spiffs is mounted
    while (!periph_spiffs_is_mounted(spiffs_handle))
    {
        vTaskDelay(500 / portTICK_PERIOD_MS);
    }

    ESP_LOGI(TAG, "[ 2 ] Start codec chip");
    audio_board_handle_t board_handle = audio_board_init();
    audio_hal_ctrl_codec(board_handle->audio_hal, AUDIO_HAL_CODEC_MODE_DECODE, AUDIO_HAL_CTRL_START);

    ESP_LOGI(TAG, "[3.0] Create audio pipeline for playback");
    audio_pipeline_cfg_t pipeline_cfg = DEFAULT_AUDIO_PIPELINE_CONFIG();
    pipeline = audio_pipeline_init(&pipeline_cfg);
    AUDIO_NULL_CHECK(TAG, pipeline, return );

    ESP_LOGI(TAG, "[3.1] Create spiffs stream to read data from sdcard");
    spiffs_stream_cfg_t flash_cfg = SPIFFS_STREAM_CFG_DEFAULT();
    flash_cfg.type = AUDIO_STREAM_READER;
    spiffs_stream_reader = spiffs_stream_init(&flash_cfg);

    ESP_LOGI(TAG, "[3.2] Create i2s stream to write data to codec chip");
    i2s_stream_cfg_t i2s_cfg = I2S_STREAM_CFG_DEFAULT();
    i2s_cfg.type = AUDIO_STREAM_WRITER;
    i2s_stream_writer = i2s_stream_init(&i2s_cfg);

    ESP_LOGI(TAG, "[3.3] Create mp3 decoder to decode mp3 file");
    mp3_decoder_cfg_t mp3_cfg = DEFAULT_MP3_DECODER_CONFIG();
    mp3_decoder = mp3_decoder_init(&mp3_cfg);

    volume_filter = create_envelope_filter();
    //envelope_volume_to(1);

    ESP_LOGI(TAG, "[3.4] Register all elements to audio pipeline");
    audio_pipeline_register(pipeline, spiffs_stream_reader, "spiffs");
    audio_pipeline_register(pipeline, mp3_decoder, "mp3");
    audio_pipeline_register(pipeline, volume_filter, "volume");
    audio_pipeline_register(pipeline, i2s_stream_writer, "i2s");

    ESP_LOGI(TAG, "[3.5] Link it together [flash]-->spiffs-->mp3_decoder-->i2s_stream-->[codec_chip]");
    const char *link_tag[4] = {"spiffs", "mp3", "volume", "i2s"};
    audio_pipeline_link(pipeline, &link_tag[0], 4);

 ESP_LOGI(TAG, "[ 4 ] Set up  event listener");
    audio_event_iface_cfg_t evt_cfg = AUDIO_EVENT_IFACE_DEFAULT_CFG();
    evt = audio_event_iface_init(&evt_cfg);

    ESP_LOGI(TAG, "[4.1] Listening event from all elements of pipeline");
    audio_pipeline_set_listener(pipeline, evt);

    ESP_LOGI(TAG, "[4.2] Listening event from peripherals");
    audio_event_iface_set_listener(esp_periph_set_get_event_iface(set), evt);
   int64_t initialization_end = esp_timer_get_time();

    printf("init took %lld\n", (initialization_end - initialization_start) /1000);


}

void play_spiffs_cleanup(void) 
{
     int64_t cleanup_start = esp_timer_get_time();

    ESP_LOGI(TAG, "[ 7 ] Stop audio_pipeline");
    audio_pipeline_stop(pipeline);
    audio_pipeline_wait_for_stop(pipeline);
    audio_pipeline_terminate(pipeline);

    audio_pipeline_unregister(pipeline, spiffs_stream_reader);
    audio_pipeline_unregister(pipeline, i2s_stream_writer);
    audio_pipeline_unregister(pipeline, mp3_decoder);
    audio_pipeline_unregister(pipeline, volume_filter);

    /* Terminal the pipeline before removing the listener */
    audio_pipeline_remove_listener(pipeline);

    /* Stop all periph before removing the listener */
    esp_periph_set_stop_all(set);
    audio_event_iface_remove_listener(esp_periph_set_get_event_iface(set), evt);

    /* Make sure audio_pipeline_remove_listener & audio_event_iface_remove_listener are called before destroying event_iface */
    audio_event_iface_destroy(evt);

    /* Release all resources */
    audio_pipeline_deinit(pipeline);
    audio_element_deinit(spiffs_stream_reader);
    audio_element_deinit(i2s_stream_writer);
    audio_element_deinit(mp3_decoder);
    audio_element_deinit(volume_filter);

    esp_periph_set_destroy(set);

    int64_t cleanup_end = esp_timer_get_time();
       printf("cleanup took %lld\n ms", (cleanup_end - cleanup_start) /1000);
}

int i = 0;
void play_spiffs_play(char *path)
{
    if(i++ == 0)
    {
        printf("initializing\n");
        play_spiffs_initialize();
        
    }
    
  
   

    
    audio_element_set_uri(spiffs_stream_reader, path);
audio_pipeline_run(pipeline);
  
   
    while (1)
    {
        audio_event_iface_msg_t msg;
        esp_err_t ret = audio_event_iface_listen(evt, &msg, portMAX_DELAY);
        if (ret != ESP_OK)
        {
            ESP_LOGE(TAG, "[ * ] Event interface error : %d", ret);
            continue;
        }

        if (msg.source_type == AUDIO_ELEMENT_TYPE_ELEMENT && msg.source == (void *)mp3_decoder && msg.cmd == AEL_MSG_CMD_REPORT_MUSIC_INFO)
        {
            audio_element_info_t music_info = {0};
            audio_element_getinfo(mp3_decoder, &music_info);

            ESP_LOGI(TAG, "[ * ] Receive music info from mp3 decoder, sample_rates=%d, bits=%d, ch=%d",
                     music_info.sample_rates, music_info.bits, music_info.channels);

            audio_element_setinfo(i2s_stream_writer, &music_info);
            i2s_stream_set_clk(i2s_stream_writer, music_info.sample_rates, music_info.bits, music_info.channels);
            continue;
        }

        /* Stop when the last pipeline element (i2s_stream_writer in this case) receives stop event */
        if (msg.source_type == AUDIO_ELEMENT_TYPE_ELEMENT && msg.source == (void *)i2s_stream_writer && msg.cmd == AEL_MSG_CMD_REPORT_STATUS && (((int)msg.data == AEL_STATUS_STATE_STOPPED) || ((int)msg.data == AEL_STATUS_STATE_FINISHED)))
        {
            printf("got here\n");
            //audio_pipeline_stop(pipeline);
            ESP_LOGW(TAG, "[ * ] Stop event received");
            break;
        }
    }
    audio_pipeline_stop(pipeline);
    audio_pipeline_wait_for_stop(pipeline);
    // esp_periph_set_stop_all(set);
    // //audio_pipeline_terminate(pipeline);
    // audio_event_iface_destroy(evt);
    // //esp_periph_set_destroy(set);
   // play_spiffs_cleanup();
}
