// WARNING:  This file contains code that is more than likely already 
// exposed from the Esp32 Arduino API.  It will be removed once integration is complete.
//
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#if defined(ARDUINO_ARCH_ESP32) 

#include "sdkconfig.h" // this sets useful config symbols, like CONFIG_IDF_TARGET_ESP32C3

// ESP32C3/S3 I2S is not supported yet due to significant changes to interface
#if !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)

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

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "freertos/queue.h"


#if ESP_IDF_VERSION_MAJOR>=4
#include "esp_intr_alloc.h"
#else
#include "esp_intr.h"
#endif

#include "soc/gpio_reg.h"
#include "soc/gpio_sig_map.h"
#include "soc/io_mux_reg.h"
#include "soc/rtc_cntl_reg.h"
#include "soc/i2s_struct.h"
#if defined(CONFIG_IDF_TARGET_ESP32)
/* included here for ESP-IDF v4.x compatibility */
#include "soc/dport_reg.h"
#endif
#include "soc/sens_reg.h"
#include "driver/gpio.h"
#include "driver/i2s.h"
#include "driver/dac.h"
#include "Esp32_i2s.h"
#include "esp32-hal.h"

#if ESP_IDF_VERSION_MAJOR<=4
#define I2S_BASE_CLK (160000000L)
#endif

#define ESP32_REG(addr) (*((volatile uint32_t*)(0x3FF00000+(addr))))

#define I2S_DMA_BLOCK_COUNT_DEFAULT      16
// 24 bytes gives us enough time if we use single stage idle
// with the two stage idle we can use the minimum of 4 bytes
#define I2S_DMA_SILENCE_SIZE     4*1 
#define I2S_DMA_SILENCE_BLOCK_COUNT  3 // two front, one back
#define I2S_DMA_QUEUE_COUNT 2

typedef struct i2s_dma_item_s {
    uint32_t  blocksize: 12;    // datalen
    uint32_t  datalen  : 12;    // len*(bits_per_sample/8)*2 => max 2047*8bit/1023*16bit samples
    uint32_t  unused   :  5;    // 0
    uint32_t  sub_sof  :  1;    // 0
    uint32_t  eof      :  1;    // 1 => last?
    uint32_t  owner    :  1;    // 1

    void* data;                // malloc(datalen)
    struct i2s_dma_item_s* next;

    // if this pointer is not null, it will be freed
    void* free_ptr;

    // if DMA buffers are preallocated
    uint8_t* buf;
} i2s_dma_item_t;

typedef struct {
        i2s_dev_t* bus;
        int8_t  ws;
        int8_t  bck;
        int8_t  out;
        int8_t  in;
        uint32_t rate;
        intr_handle_t isr_handle;
        xQueueHandle tx_queue;

        uint8_t* silence_buf;
        size_t silence_len;

        i2s_dma_item_t* dma_items;
        size_t dma_count;
        uint32_t dma_buf_len :12;
        uint32_t unused      :20;
        volatile uint32_t is_sending_data;
} i2s_bus_t;

// is_sending_data values
#define I2s_Is_Idle 0
#define I2s_Is_Pending 1
#define I2s_Is_Sending 2

static uint8_t i2s_silence_buf[I2S_DMA_SILENCE_SIZE] = { 0 };

#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
// (I2S_NUM_MAX == 2)
static i2s_bus_t I2S[I2S_NUM_MAX] = {
    {&I2S0, -1, -1, -1, -1, 0, NULL, NULL, i2s_silence_buf, I2S_DMA_SILENCE_SIZE, NULL, I2S_DMA_BLOCK_COUNT_DEFAULT, 0, 0, I2s_Is_Idle},
    {&I2S1, -1, -1, -1, -1, 0, NULL, NULL, i2s_silence_buf, I2S_DMA_SILENCE_SIZE, NULL, I2S_DMA_BLOCK_COUNT_DEFAULT, 0, 0, I2s_Is_Idle}
};
#else
static i2s_bus_t I2S[I2S_NUM_MAX] = {
    {&I2S0, -1, -1, -1, -1, 0, NULL, NULL, i2s_silence_buf, I2S_DMA_SILENCE_SIZE, NULL, I2S_DMA_BLOCK_COUNT_DEFAULT, 0, 0, I2s_Is_Idle}
};
#endif

void IRAM_ATTR i2sDmaISR(void* arg);

bool i2sInitDmaItems(uint8_t bus_num) {
    if (bus_num >= I2S_NUM_MAX) {
        return false;
    }
    if (I2S[bus_num].tx_queue) {// already set
        return true;
    }

    size_t dmaCount = I2S[bus_num].dma_count;

    if (I2S[bus_num].dma_items == NULL) {
        I2S[bus_num].dma_items = (i2s_dma_item_t*)heap_caps_malloc(dmaCount * sizeof(i2s_dma_item_t), MALLOC_CAP_DMA);
        if (I2S[bus_num].dma_items == NULL) {
            log_e("MEM ERROR!");
            return false;
        }
    }

    int i, i2;
    i2s_dma_item_t* item = NULL;
    i2s_dma_item_t* itemPrev = NULL;

    for(i=0; i< dmaCount; i++) {
        itemPrev = item;

        i2 = (i+1) % dmaCount;
        item = &I2S[bus_num].dma_items[i];
        item->eof = 0;
        item->owner = 1;
        item->sub_sof = 0;
        item->unused = 0;
        item->data = I2S[bus_num].silence_buf;
        item->blocksize = I2S[bus_num].silence_len;
        item->datalen = I2S[bus_num].silence_len;
        item->next = &I2S[bus_num].dma_items[i2];
        item->free_ptr = NULL;
        item->buf = NULL;
    }
    itemPrev->eof = 1;
    item->eof = 1;

    I2S[bus_num].tx_queue = xQueueCreate(I2S_DMA_QUEUE_COUNT, sizeof(i2s_dma_item_t*));
    if (I2S[bus_num].tx_queue == NULL) {// memory error
        log_e("MEM ERROR!");
        free(I2S[bus_num].dma_items);
        I2S[bus_num].dma_items = NULL;
        return false;
    }
    return true;
}

esp_err_t i2sSetClock(uint8_t bus_num, uint8_t div_num, uint8_t div_b, uint8_t div_a, uint8_t bck, uint8_t bits) {
    if (bus_num >= I2S_NUM_MAX || div_a > 63 || div_b > 63 || bck > 63) {
        return ESP_FAIL;
    }
    i2s_dev_t* i2s = I2S[bus_num].bus;

    typeof(i2s->clkm_conf) clkm_conf;

    clkm_conf.val = 0;
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
    clkm_conf.clka_en = 0;
#else
    clkm_conf.clk_sel = 2;
#endif

    clkm_conf.clkm_div_a = div_a;
    clkm_conf.clkm_div_b = div_b;
    clkm_conf.clkm_div_num = div_num;
    i2s->clkm_conf.val = clkm_conf.val;

    typeof(i2s->sample_rate_conf) sample_rate_conf;
    sample_rate_conf.val = 0;
    sample_rate_conf.tx_bck_div_num = bck;
    sample_rate_conf.rx_bck_div_num = bck;
    sample_rate_conf.tx_bits_mod = bits;
    sample_rate_conf.rx_bits_mod = bits;
    i2s->sample_rate_conf.val = sample_rate_conf.val;
    return ESP_OK;
}

void i2sSetPins(uint8_t bus_num, int8_t out, bool invert) {
    if (bus_num >= I2S_NUM_MAX) {
        return;
    }

    if (out >= 0) {
        if (I2S[bus_num].out != out) {
            if (I2S[bus_num].out >= 0) {
                gpio_matrix_out(I2S[bus_num].out, 0x100, invert, false);
            }
            I2S[bus_num].out = out;
            pinMode(out, OUTPUT);

            int i2sSignal;
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
//            (I2S_NUM_MAX == 2)
            if (bus_num == 1) {
                i2sSignal = I2S1O_DATA_OUT23_IDX;
            }
            else
#endif
            {
                i2sSignal = I2S0O_DATA_OUT23_IDX;
            }

            gpio_matrix_out(out, i2sSignal, invert, false);
        }
    } else if (I2S[bus_num].out >= 0) {
        gpio_matrix_out(I2S[bus_num].out, 0x100, invert, false);
        I2S[bus_num].out = -1;
    }

}

bool i2sWriteDone(uint8_t bus_num) {
    if (bus_num >= I2S_NUM_MAX) {
        return false;
    }

    return (I2S[bus_num].is_sending_data == I2s_Is_Idle);
}

void i2sInit(uint8_t bus_num, 
        uint32_t bits_per_sample, 
        uint32_t sample_rate, 
        i2s_tx_chan_mod_t chan_mod, 
        i2s_tx_fifo_mod_t fifo_mod, 
        size_t dma_count, 
        size_t dma_len) {
    if (bus_num >= I2S_NUM_MAX) {
        return;
    }

    I2S[bus_num].dma_count = dma_count + I2S_DMA_SILENCE_BLOCK_COUNT; // an extra two for looping silence
    I2S[bus_num].dma_buf_len = dma_len & 0xFFF;

    if (!i2sInitDmaItems(bus_num)) {
        return;
    }

#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
// (I2S_NUM_MAX == 2)
    if (bus_num) {
        periph_module_enable(PERIPH_I2S1_MODULE);
    } else 
#endif
    {
        periph_module_enable(PERIPH_I2S0_MODULE);
    }

    esp_intr_disable(I2S[bus_num].isr_handle);
    i2s_dev_t* i2s = I2S[bus_num].bus;
    i2s->out_link.stop = 1;
    i2s->conf.tx_start = 0;
    i2s->int_ena.val = 0;
    i2s->int_clr.val = 0xFFFFFFFF;
    i2s->fifo_conf.dscr_en = 0;

    // reset fifo
    i2s->conf.rx_fifo_reset = 1;
    i2s->conf.rx_fifo_reset = 0;
    i2s->conf.tx_fifo_reset = 1;
    i2s->conf.tx_fifo_reset = 0;

    // reset i2s
    i2s->conf.tx_reset = 1;
    i2s->conf.tx_reset = 0;
    i2s->conf.rx_reset = 1;
    i2s->conf.rx_reset = 0;

    // reset dma
    i2s->lc_conf.in_rst = 1;
    i2s->lc_conf.in_rst = 0;
    i2s->lc_conf.out_rst = 1;
    i2s->lc_conf.out_rst = 0;

    // Enable and configure DMA
    typeof(i2s->lc_conf) lc_conf;
    lc_conf.val = 0;
    lc_conf.out_eof_mode = 1;
    i2s->lc_conf.val = lc_conf.val;

#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
    i2s->pdm_conf.pcm2pdm_conv_en = 0;
    i2s->pdm_conf.pdm2pcm_conv_en = 0;
#endif
    // SET_PERI_REG_BITS(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_SOC_CLK_SEL, 0x1, RTC_CNTL_SOC_CLK_SEL_S);

    typeof(i2s->conf_chan) conf_chan;
    conf_chan.val = 0;
    conf_chan.tx_chan_mod = chan_mod; //  0-two channel;1-right;2-left;3-righ;4-left
    conf_chan.rx_chan_mod = chan_mod; //  0-two channel;1-right;2-left;3-righ;4-left
    i2s->conf_chan.val = conf_chan.val;

    typeof(i2s->fifo_conf) fifo_conf;
    fifo_conf.val = 0;
    fifo_conf.tx_fifo_mod = fifo_mod; //  0-right&left channel;1-one channel
    fifo_conf.rx_fifo_mod = fifo_mod; //  0-right&left channel;1-one channel
    i2s->fifo_conf.val = fifo_conf.val;

    typeof(i2s->conf) conf;
    conf.val = 0;
    conf.tx_msb_shift = (bits_per_sample != 8);// 0:DAC/PCM, 1:I2S
    conf.tx_right_first = (bits_per_sample == 8);
    i2s->conf.val = conf.val;

    typeof(i2s->conf2) conf2;
    conf2.val = 0;
    conf2.lcd_en = (bits_per_sample == 8);
    i2s->conf2.val = conf2.val;

    i2s->fifo_conf.tx_fifo_mod_force_en = 1;

#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
    i2s->pdm_conf.rx_pdm_en = 0;
    i2s->pdm_conf.tx_pdm_en = 0;
#endif

    i2sSetSampleRate(bus_num, sample_rate, bits_per_sample);

    //  enable intr in cpu // 
    int i2sIntSource;

#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
//    (I2S_NUM_MAX == 2)
    if (bus_num == 1) {
        i2sIntSource = ETS_I2S1_INTR_SOURCE;
    }
    else
#endif
    {
        i2sIntSource = ETS_I2S0_INTR_SOURCE;
    }


    esp_intr_alloc(i2sIntSource, ESP_INTR_FLAG_IRAM | ESP_INTR_FLAG_LEVEL1, &i2sDmaISR, &I2S[bus_num], &I2S[bus_num].isr_handle);
    //  enable send intr
    i2s->int_ena.out_eof = 1;
    i2s->int_ena.out_dscr_err = 1;

    i2s->fifo_conf.dscr_en = 1;// enable dma
    i2s->out_link.start = 0;
    i2s->out_link.addr = (uint32_t)(&I2S[bus_num].dma_items[0]); // loads dma_struct to dma
    i2s->out_link.start = 1; // starts dma
    i2s->conf.tx_start = 1;// Start I2s module

    esp_intr_enable(I2S[bus_num].isr_handle);
}

esp_err_t i2sSetSampleRate(uint8_t bus_num, uint32_t rate, uint8_t bits) {
    if (bus_num >= I2S_NUM_MAX) {
        return ESP_FAIL;
    }

    if (I2S[bus_num].rate == rate) {
        return ESP_OK;
    }

    int clkmInteger, clkmDecimals, bck = 0;
    double denom = (double)1 / 63;
    int channel = 2;

//    double mclk;
    double clkmdiv;

    int factor;

    if (bits == 8) {
        factor = 120;
    } else {
        factor = (256 % bits) ? 384 : 256;
    }

    clkmdiv = (double)I2S_BASE_CLK / (rate* factor);
    if (clkmdiv > 256) {
        log_e("rate is too low");
        return ESP_FAIL;
    }
    I2S[bus_num].rate = rate;

    clkmInteger = clkmdiv;
    clkmDecimals = ((clkmdiv - clkmInteger) / denom);

    if (bits == 8) {
//        mclk = rate* factor;
        bck = 60;
        bits = 16;
    } else {
//        mclk = (double)clkmInteger + (denom* clkmDecimals);
        bck = factor/(bits* channel);
    }

    i2sSetClock(bus_num, clkmInteger, clkmDecimals, 63, bck, bits);

    return ESP_OK;
}



void IRAM_ATTR i2sDmaISR(void* arg)
{
    i2s_bus_t* dev = (i2s_bus_t*)(arg);

    if (dev->bus->int_st.out_eof) 
    {
 //       i2s_dma_item_t* item = (i2s_dma_item_t*)(dev->bus->out_eof_des_addr);
        if (dev->is_sending_data == I2s_Is_Pending)
        {
            dev->is_sending_data = I2s_Is_Idle;
        }
        else if (dev->is_sending_data == I2s_Is_Sending)
        {
            // loop the silent items
            i2s_dma_item_t* itemSilence = &dev->dma_items[1];
            itemSilence->next = &dev->dma_items[0];

            dev->is_sending_data = I2s_Is_Pending;
        }
    }

    dev->bus->int_clr.val = dev->bus->int_st.val;
}

size_t i2sWrite(uint8_t bus_num, uint8_t* data, size_t len, bool copy, bool free_when_sent) {
    if (bus_num >= I2S_NUM_MAX || !I2S[bus_num].tx_queue) {
        return 0;
    }
    size_t blockSize = len;

    i2s_dma_item_t* item = &I2S[bus_num].dma_items[0]; 
    size_t dataLeft = len;
    uint8_t* pos = data;

    // skip front two silent items
    item += 2;

    while (dataLeft) {
        
        blockSize = dataLeft;
        if (blockSize > I2S_DMA_MAX_DATA_LEN) {
            blockSize = I2S_DMA_MAX_DATA_LEN;
        }
        dataLeft -= blockSize;

        // data is constant. no need to copy
        item->data = pos;
        item->blocksize = blockSize;
        item->datalen = blockSize;

        item++;

        pos += blockSize;
    }


    // reset silence item to not loop
    item = &I2S[bus_num].dma_items[1];
    item->next = &I2S[bus_num].dma_items[2];
    I2S[bus_num].is_sending_data = I2s_Is_Sending;
        

    xQueueReset(I2S[bus_num].tx_queue);
    xQueueSend(I2S[bus_num].tx_queue, (void*)&I2S[bus_num].dma_items[0], 10);

    return len;
}

#endif // !defined(CONFIG_IDF_TARGET_ESP32C3)
#endif // defined(ARDUINO_ARCH_ESP32) 

