#include <stdio.h>
#include <math.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/i2c.h"
#include "driver/i2s_std.h"
#include "TAS5720.h"
#include "esp_log.h"
#include "driver/gpio.h"
#include "driver/i2s_common.h"

#define TAG_I2S "I2S"

#define SAMPLE_RATE (48000)
#define WAVE_FREQ 1000 // Frequência da onda senoidal
#define WAVE_SAMPLE_CNT (SAMPLE_RATE / WAVE_FREQ)
#define SAMPLE_BITS 16
#define PI (3.14159265)
#define I2S_NUM (0)
#define I2S_BCK_IO (4)
#define I2S_WS_IO (5)
#define I2S_DO_IO (6)
#define I2C_SCL_IO (9)
#define I2C_SDA_IO (8)
#define I2C_FREQ_HZ (100000)

#define I2C_DEVICE_ADDR 0x6C // I2C device address in hexadecimal

#define I2S_STD_MSB_SLOT_TAS_CONFIG(bits_per_sample, mono_or_stereo) \
    {                                                                \
        .data_bit_width = bits_per_sample,                           \
        .slot_bit_width = I2S_SLOT_BIT_WIDTH_32BIT,                  \
        .slot_mode = mono_or_stereo,                                 \
        .slot_mask = I2S_STD_SLOT_BOTH,                              \
        .ws_width = bits_per_sample * 2,                             \
        .ws_pol = false,                                             \
        .bit_shift = false,                                          \
        .left_align = true,                                          \
        .big_endian = false,                                         \
        .bit_order_lsb = false                                       \
    }
TAS5720_t tas;

i2s_chan_handle_t tx_handle;
/* Get the default channel configuration by the helper macro.
 * This helper macro is defined in `i2s_common.h` and shared by all the I2S communication modes.
 * It can help to specify the I2S role and port ID */
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_0, I2S_ROLE_MASTER);
/* Allocate a new TX channel and get the handle of this channel */

void i2c_init()
{
    i2c_config_t conf = {
        .mode = I2C_MODE_MASTER,
        .sda_io_num = I2C_SDA_IO,
        .sda_pullup_en = GPIO_PULLUP_ENABLE,
        .scl_io_num = I2C_SCL_IO,
        .scl_pullup_en = GPIO_PULLUP_ENABLE,
        .master.clk_speed = I2C_FREQ_HZ,
    };
    i2c_param_config(I2C_NUM_0, &conf);
    i2c_driver_install(I2C_NUM_0, conf.mode, 0, 0, 0);
}

void i2s_init()
{
    i2s_std_config_t std_cfg = {
        .clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(48000),
        .slot_cfg = I2S_STD_MSB_SLOT_TAS_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_STEREO),
        .gpio_cfg = {
            .mclk = I2S_GPIO_UNUSED,
            .bclk = GPIO_NUM_4,
            .ws = GPIO_NUM_5,
            .dout = GPIO_NUM_6,
            .din = I2S_GPIO_UNUSED,
            .invert_flags = {
                .mclk_inv = false,
                .bclk_inv = false,
                .ws_inv = false,
            },
        },
    };

    /*std_cfg.clk_cfg.sample_rate_hz = SAMPLE_RATE;              // LRCLK = 48 kHz
    std_cfg.slot_cfg.data_bit_width = SAMPLE_BITS;             // 16 bits por amostra
    std_cfg.slot_cfg.slot_bit_width = I2S_SLOT_BIT_WIDTH_32BIT; // 32 bits por slot
    std_cfg.slot_cfg.slot_mode = I2S_SLOT_MODE_STEREO;*/
    /*Initialize the channel */
    i2s_new_channel(&chan_cfg, &tx_handle, NULL);
    i2s_channel_init_std_mode(tx_handle, &std_cfg);
    /* Before writing data, start the TX channel first */
    i2s_channel_disable(tx_handle);
}

void generate_sine_wave(int16_t *buffer, int sample_rate, int frequency, int samples)
{
    for (int i = 0; i < samples; i++)
    {
        float sample = sinf(2.0f * PI * frequency * i / sample_rate);
        buffer[i] = (int16_t)(sample * 32767.0f);
    }
}

void generate_square_wave(int16_t *buffer, int sample_rate, int frequency, int samples)
{
    int half_period = sample_rate / (2 * frequency);
    for (int i = 0; i < samples; i++)
    {
        buffer[i] = (i / half_period) % 2 == 0 ? 32767 : -32767;
    }
}

int16_t sine_wave[WAVE_SAMPLE_CNT];

void generate_sine_wave2(float volume)
{
    int16_t signed_sample = 0;
    for (int i = 0; i < WAVE_SAMPLE_CNT; i++)
    {
        signed_sample = (int16_t)(32767 * sin(2 * PI * i / WAVE_SAMPLE_CNT));
        sine_wave[i] = signed_sample * volume;
    }
}

void app_main()
{
    uint8_t teste = 4;
    //vTaskDelay(5000 / portTICK_PERIOD_MS);
    i2c_init();

    TAS5720_init(&tas, I2C_NUM_0, I2C_DEVICE_ADDR, true);
    //TAS5720_mute(&tas, true);
        //TAS5720_setSerialAudioInterfaceFormat(&tas, SAI_I2S);
    //TAS5720_setChannelSelection(&tas, RIGHT);
    //TAS5720_setAnalogGain(&tas, GAIN_25_DBV);
    //TAS5720_setDigitalBoost(&tas, DIGITAL_BOOST_6DB);
    //TAS5720_setVolume(&tas, 255, 255);
    //TAS5720_mute(&tas, false);

    //TAS5720_setShutdown(&tas, true);
    //TAS5720_setPWMRate(&tas, RATE_16_LRCK);
    TAS5720_write(&tas, TAS5720_ADDRESS_POWER_CONTROL, 0xFD);
    TAS5720_getVolume(&tas);
    ESP_LOGE("TAG INFO", "volume é de %hhu", tas.volumeLeft);

    uint8_t teste2 = TAS5720_read(&tas, TAS5720_ADDRESS_FAULT_CONFIGURATION_ERROR_STATUS);
    ESP_LOGE("TAG INFO", "fault é de %hhu", teste2);

    teste = TAS5720_getAnalogGain(&tas);
    ESP_LOGE("TAG INFO", "ganho é de %d", teste);

    teste = TAS5720_getShutdown(&tas);
    ESP_LOGE("TAG INFO", "shutdown é de %d", teste);
    vTaskDelay(5000 / portTICK_PERIOD_MS);
    teste2 = TAS5720_read(&tas, TAS5720_ADDRESS_FAULT_CONFIGURATION_ERROR_STATUS);
    ESP_LOGE("TAG INFO", "fault é de %hhu", teste2);
    teste2 = TAS5720_read(&tas, TAS5720_ADDRESS_DEVICE_IDENTIFICATION);
    ESP_LOGE("TAG INFO", "ADDR 0 :  %hhu", teste2);
    teste2 = TAS5720_read(&tas, TAS5720_ADDRESS_POWER_CONTROL);
    ESP_LOGE("TAG INFO", "ADDR 1 :  %hhu", teste2);
    TAS5720_setShutdown(&tas, false);
    teste2 = TAS5720_read(&tas, TAS5720_ADDRESS_POWER_CONTROL);
    ESP_LOGE("TAG INFO", "ADDR 1 :  %hhu", teste2);
    teste2 = TAS5720_read(&tas, TAS5720_ADDRESS_DIGITAL_CONTROL);
    ESP_LOGE("TAG INFO", "ADDR 2 :  %hhu", teste2);
    teste2 = TAS5720_read(&tas, TAS5720_ADDRESS_VOLUME_CONTROL_CONFIGURATION);
    ESP_LOGE("TAG INFO", "ADDR 3 :  %hhu", teste2);
    teste2 = TAS5720_read(&tas, TAS5720_ADDRESS_VOLUME_CONTROL_LEFT);
    ESP_LOGE("TAG INFO", "ADDR 4 :  %hhu", teste2);
    teste2 = TAS5720_read(&tas, TAS5720_ADDRESS_ANALOG_CONTROL);
    ESP_LOGE("TAG INFO", "ADDR 6 :  %hhu", teste2);
    teste2 = TAS5720_read(&tas, TAS5720_ADDRESS_FAULT_CONFIGURATION_ERROR_STATUS);
    ESP_LOGE("TAG INFO", "ADDR 8 :  %hhu", teste2);
    teste2 = TAS5720_read(&tas, TAS5720_ADDRESS_DIGITAL_CLIPPER_2);
    ESP_LOGE("TAG INFO", "ADDR 10 :  %hhu", teste2);
    teste2 = TAS5720_read(&tas, TAS5720_ADDRESS_DIGITAL_CLIPPER_1);
    ESP_LOGE("TAG INFO", "ADDR 11 :  %hhu", teste2);

    i2s_init();

    // int num_samples = 44100;
    // int16_t *audio_buffer = malloc(WAVE_SAMPLE_CNT * sizeof(int16_t));  // buffer estereo
    // generate_sine_wave(audio_buffer, num_samples, 440.0f, num_samples); // Gera uma onda senoidal de 440 Hz
    size_t bytes_written;
    generate_sine_wave2(1);
    /*while (1)
    {

        ESP_ERROR_CHECK(i2s_write(I2S_NUM, audio_buffer, num_samples * sizeof(int16_t), &bytes_written, portMAX_DELAY));

        vTaskDelay(pdMS_TO_TICKS(100));
        uint8_t teste2 = TAS5720_read(&tas, TAS5720_ADDRESS_FAULT_CONFIGURATION_ERROR_STATUS);
        ESP_LOGI("TAG INFO", "fault é de %hhu", teste2);
    }*/
    i2s_channel_enable(tx_handle);
    while (1)
    {
        i2s_channel_write(tx_handle, sine_wave, sizeof(sine_wave), &bytes_written, portMAX_DELAY);
        //teste2 = TAS5720_read(&tas, TAS5720_ADDRESS_FAULT_CONFIGURATION_ERROR_STATUS);
        //ESP_LOGI("TAG INFO", "fault é de %hhu", teste2);
    }
}
