/* Audio passthru

   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 "audio_pipeline.h"
#include "i2s_stream.h"
#include "board.h"

#include "equalizer.h"
#include "filter_resample.h"
#include "input_key_service.h"

#define INPUT_PLAYBACK_RATE       48000
#define INPUT_PLAYBACK_CHANNEL    2

#define OUTPUT_PLAYBACK_RATE       48000
#define OUTPUT_PLAYBACK_CHANNEL    1

static const char* AUDIO_PIPELINE_ID_STREAM_READER="i2s_reader";
static const char* AUDIO_PIPELINE_ID_RESAMPLE_FILTER="filter";
static const char* AUDIO_PIPELINE_ID_EQUALIZER="equalizer";
static const char* AUDIO_PIPELINE_ID_STREAM_WRITER="i2s_writer";

static const char* TAG = "SMART_SPEAKER";

static const int VOLUME_INCREMENT=10;
static const int VOLUME_INITIAL=90;
static const int VOLUME_MAX=100;
static const int VOLUME_MIN=0;

audio_element_handle_t audio_element_handle_equalizer;
int equalizer_level=0;
esp_err_t updateEqualizer(int value_gain);

static esp_err_t input_key_service_cb(periph_service_handle_t handle, periph_service_event_t *periph_service_event, void *ctx){
	ESP_LOGI(TAG, "input_key_service_cb");
	
    audio_board_handle_t audio_board_handle = (audio_board_handle_t) ctx;
    int player_volume;
    audio_hal_get_volume(audio_board_handle->audio_hal, &player_volume);
	
	if (periph_service_event->type == INPUT_KEY_SERVICE_ACTION_CLICK_RELEASE) {
        ESP_LOGI(TAG, "[ * ] input key id is %d", (int)periph_service_event->data);
        switch ((int)periph_service_event->data) {
			
            case INPUT_KEY_USER_ID_PLAY:
                ESP_LOGI(TAG, "[ * ] [Play] input key event");
				equalizer_level--;
				if(equalizer_level>-20){
					equalizer_level--;
					updateEqualizer(equalizer_level);
				}
/*                 audio_element_state_t el_state = audio_element_get_state(audio_element_handle_i2s_stream_writer);
                switch (el_state) {
                    case AEL_STATE_INIT :
                        ESP_LOGI(TAG, "[ * ] Starting audio pipeline");
                        audio_pipeline_run(pipeline);
                        break;
                    case AEL_STATE_RUNNING :
                        ESP_LOGI(TAG, "[ * ] Pausing audio pipeline");
                        audio_pipeline_pause(pipeline);
                        break;
                    case AEL_STATE_PAUSED :
                        ESP_LOGI(TAG, "[ * ] Resuming audio pipeline");
                        audio_pipeline_resume(pipeline);
                        break;
                    default :
                        ESP_LOGI(TAG, "[ * ] Not supported state %d", el_state);
                } */
                break;
				
            case INPUT_KEY_USER_ID_SET:
                ESP_LOGI(TAG, "[ * ] [Set] input key event");
				equalizer_level++;
				if(equalizer_level<0){
					equalizer_level++;
					updateEqualizer(equalizer_level);
				}
/*                 audio_pipeline_terminate(pipeline);
                ESP_LOGI(TAG, "[ * ] Stopped, advancing to the next song");
                get_file(NEXT);
                audio_pipeline_run(pipeline); */
                break;
				
            case INPUT_KEY_USER_ID_VOLUP:
                ESP_LOGI(TAG, "[ * ] [Vol+] input key event");
                player_volume += VOLUME_INCREMENT;
                if (player_volume > VOLUME_MAX) {
                    player_volume = VOLUME_MAX;
                }
                audio_hal_set_volume(audio_board_handle->audio_hal, player_volume);
                ESP_LOGI(TAG, "[ * ] Volume set to %d %%", player_volume);
                break;
				
            case INPUT_KEY_USER_ID_VOLDOWN:
                ESP_LOGI(TAG, "[ * ] [Vol-] input key event");
                player_volume -= VOLUME_INCREMENT;
                if (player_volume < VOLUME_MIN) {
                    player_volume = VOLUME_MIN;
                }
                audio_hal_set_volume(audio_board_handle->audio_hal, player_volume);
                ESP_LOGI(TAG, "[ * ] Volume set to %d %%", player_volume);
                break;
        }
    }
	
	return ESP_OK;
}

void app_main(void){
    audio_pipeline_handle_t audio_pipeline_handle;
    audio_element_handle_t audio_element_handle_i2s_stream_reader, audio_element_handle_i2s_stream_writer, audio_element_handle_rsp_filter;

    esp_log_level_set("*", ESP_LOG_INFO);
    esp_log_level_set(TAG, ESP_LOG_DEBUG);
	
    ESP_LOGI(TAG, "[1.1] Start codec chip");
    audio_board_handle_t audio_board_handle = audio_board_init();
    audio_hal_ctrl_codec(audio_board_handle->audio_hal, AUDIO_HAL_CODEC_MODE_LINE_IN, AUDIO_HAL_CTRL_START);

	audio_hal_set_volume(audio_board_handle->audio_hal, VOLUME_INITIAL);
	
    ESP_LOGI(TAG, "[2.1] Initialize peripherals management");
    esp_periph_config_t esp_periph_config = DEFAULT_ESP_PERIPH_SET_CONFIG();
    esp_periph_set_handle_t esp_periph_set_handle = esp_periph_set_init(&esp_periph_config);

    ESP_LOGI(TAG, "[2.2] Initialize and start peripherals");
    audio_board_key_init(esp_periph_set_handle);
	
    ESP_LOGI(TAG, "[2.3] Create and start input key service");
    input_key_service_info_t input_key_service_info[] = INPUT_KEY_DEFAULT_INFO();
    periph_service_handle_t periph_service_handle = input_key_service_create(esp_periph_set_handle);
    input_key_service_add_key(periph_service_handle, input_key_service_info, INPUT_KEY_NUM);
    periph_service_set_callback(periph_service_handle, input_key_service_cb, (void *)audio_board_handle);

    ESP_LOGI(TAG, "[3.1] Create audio pipeline for playback");
    audio_pipeline_cfg_t audio_pipeline_cfg = DEFAULT_AUDIO_PIPELINE_CONFIG();
    audio_pipeline_handle = audio_pipeline_init(&audio_pipeline_cfg);

    ESP_LOGI(TAG, "[3.2] Create i2s stream to read data from codec chip");
    i2s_stream_cfg_t i2s_stream_cfg_reader = I2S_STREAM_CFG_DEFAULT();
	
	/*
		I2S_CHANNEL_FMT_RIGHT_LEFT (default), I2S_CHANNEL_FMT_ALL_RIGHT, I2S_CHANNEL_FMT_ALL_LEFT, I2S_CHANNEL_FMT_ONLY_RIGHT, I2S_CHANNEL_FMT_ONLY_LEFT
	*/
	//	i2s_stream_cfg_reader.i2s_config.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT;
	
	i2s_stream_cfg_reader.i2s_config.sample_rate=INPUT_PLAYBACK_RATE;
    i2s_stream_cfg_reader.type = AUDIO_STREAM_READER;
    audio_element_handle_i2s_stream_reader = i2s_stream_init(&i2s_stream_cfg_reader);
	
	rsp_filter_cfg_t rsp_filter_cfg = DEFAULT_RESAMPLE_FILTER_CONFIG();
    rsp_filter_cfg.src_rate = INPUT_PLAYBACK_RATE;
    rsp_filter_cfg.src_ch = INPUT_PLAYBACK_CHANNEL;
    rsp_filter_cfg.dest_rate = OUTPUT_PLAYBACK_RATE;
    rsp_filter_cfg.dest_ch = OUTPUT_PLAYBACK_CHANNEL;
    rsp_filter_cfg.type = AUDIO_CODEC_TYPE_DECODER;
    audio_element_handle_rsp_filter = rsp_filter_init(&rsp_filter_cfg);
	
	ESP_LOGI(TAG, "[3.3] Create equalizer");
    equalizer_cfg_t equalizer_cfg = DEFAULT_EQUALIZER_CONFIG();
	equalizer_cfg.channel=INPUT_PLAYBACK_CHANNEL;
	equalizer_cfg.samplerate=INPUT_PLAYBACK_RATE;
	// The size of gain array should be the multiplication of NUMBER_BAND and number channels of audio stream data. The default gain is -13 dB.
    int set_gain[] = { equalizer_level, equalizer_level, equalizer_level, equalizer_level, equalizer_level, equalizer_level, equalizer_level, equalizer_level, equalizer_level, equalizer_level, equalizer_level, equalizer_level, equalizer_level, equalizer_level, equalizer_level, equalizer_level, equalizer_level, equalizer_level, equalizer_level, equalizer_level};
    equalizer_cfg.set_gain = set_gain; 
    audio_element_handle_equalizer = equalizer_init(&equalizer_cfg);

    ESP_LOGI(TAG, "[3.4] Create i2s stream to write data to codec chip");
    i2s_stream_cfg_t i2s_stream_cfg_writer = I2S_STREAM_CFG_DEFAULT();
	
	i2s_stream_cfg_writer.i2s_config.sample_rate=OUTPUT_PLAYBACK_RATE;
	/*
		I2S_CHANNEL_FMT_RIGHT_LEFT (default), I2S_CHANNEL_FMT_ALL_RIGHT, I2S_CHANNEL_FMT_ALL_LEFT, I2S_CHANNEL_FMT_ONLY_RIGHT, I2S_CHANNEL_FMT_ONLY_LEFT
	*/
	//	i2s_stream_cfg_writer.i2s_config.channel_format = I2S_CHANNEL_FMT_ONLY_LEFT;
	
    i2s_stream_cfg_writer.type = AUDIO_STREAM_WRITER;
    audio_element_handle_i2s_stream_writer = i2s_stream_init(&i2s_stream_cfg_writer);

    ESP_LOGI(TAG, "[3.5] Register all elements to audio pipeline");
    audio_pipeline_register(audio_pipeline_handle, audio_element_handle_i2s_stream_reader, AUDIO_PIPELINE_ID_STREAM_READER);
    audio_pipeline_register(audio_pipeline_handle, audio_element_handle_rsp_filter, AUDIO_PIPELINE_ID_RESAMPLE_FILTER);
	audio_pipeline_register(audio_pipeline_handle, audio_element_handle_equalizer, AUDIO_PIPELINE_ID_EQUALIZER);
    audio_pipeline_register(audio_pipeline_handle, audio_element_handle_i2s_stream_writer, AUDIO_PIPELINE_ID_STREAM_WRITER);

    ESP_LOGI(TAG, "[3.6] Link it together [codec_chip]-->i2s_stream_reader-->equalizer-->i2s_stream_writer-->[codec_chip]");
    //	audio_pipeline_link(audio_pipeline_handle, (const char *[]) {AUDIO_PIPELINE_ID_STREAM_READER, AUDIO_PIPELINE_ID_EQUALIZER, AUDIO_PIPELINE_ID_RESAMPLE_FILTER, AUDIO_PIPELINE_ID_STREAM_WRITER}, 4);
    audio_pipeline_link(audio_pipeline_handle, (const char *[]) {AUDIO_PIPELINE_ID_STREAM_READER, AUDIO_PIPELINE_ID_EQUALIZER, AUDIO_PIPELINE_ID_STREAM_WRITER}, 3);

    ESP_LOGI(TAG, "[4.1] Set up  event listener");
    audio_event_iface_cfg_t evt_cfg = AUDIO_EVENT_IFACE_DEFAULT_CFG();
    audio_event_iface_handle_t audio_event_iface_handle = audio_event_iface_init(&evt_cfg);

    ESP_LOGI(TAG, "[4.2] Listening event from all elements of pipeline");
    audio_pipeline_set_listener(audio_pipeline_handle, audio_event_iface_handle);

    ESP_LOGI(TAG, "[5] Start audio_pipeline");
    audio_pipeline_run(audio_pipeline_handle);

    ESP_LOGI(TAG, "[6] Listen for all pipeline events");
    while (1) {
        audio_event_iface_msg_t msg;
        esp_err_t ret = audio_event_iface_listen(audio_event_iface_handle, &msg, portMAX_DELAY);
        if (ret != ESP_OK) {
            ESP_LOGE(TAG, "[ * ] Event interface error : %d", ret);
            continue;
        }

        if (msg.cmd == AEL_MSG_CMD_ERROR) {
            ESP_LOGE(TAG, "[ * ] Action command error: src_type:%d, source:%p cmd:%d, data:%p, data_len:%d",
                     msg.source_type, msg.source, msg.cmd, msg.data, msg.data_len);
        }

        /* 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 *) audio_element_handle_i2s_stream_writer
            && msg.cmd == AEL_MSG_CMD_REPORT_STATUS
            && (((int)msg.data == AEL_STATUS_STATE_STOPPED) || ((int)msg.data == AEL_STATUS_STATE_FINISHED))) {
            ESP_LOGW(TAG, "[ * ] Stop event received");
            break;
        }
    }

    ESP_LOGI(TAG, "[7] Stop audio_pipeline");
    /* Terminate the pipeline before removing the listener */
    audio_pipeline_terminate(audio_pipeline_handle);

    audio_pipeline_unregister(audio_pipeline_handle, audio_element_handle_i2s_stream_reader);
    audio_pipeline_unregister(audio_pipeline_handle, audio_element_handle_equalizer);
    audio_pipeline_unregister(audio_pipeline_handle, audio_element_handle_rsp_filter);
    audio_pipeline_unregister(audio_pipeline_handle, audio_element_handle_i2s_stream_writer);

    audio_pipeline_remove_listener(audio_pipeline_handle);

    /* Make sure audio_pipeline_remove_listener & audio_event_iface_remove_listener are called before destroying event_iface */
    audio_event_iface_destroy(audio_event_iface_handle);

    /* Release all resources */
    audio_pipeline_deinit(audio_pipeline_handle);
	
    audio_element_deinit(audio_element_handle_i2s_stream_reader);
    audio_element_deinit(audio_element_handle_equalizer);
    audio_element_deinit(audio_element_handle_rsp_filter);
    audio_element_deinit(audio_element_handle_i2s_stream_writer);
}

esp_err_t updateEqualizer(int value_gain){
	bool is_channels_gain_equal=true;
	for(int index=0; index<10; index++){
		esp_err_t esp_err=equalizer_set_gain_info(audio_element_handle_equalizer, index, value_gain, is_channels_gain_equal);
		if(esp_err!=ESP_OK){
			ESP_LOGW(TAG, "error while setting equalizer value");
			return esp_err;
		}
	}
	return ESP_OK;
}
