/*
 * Copyright (c) 2024 Croxel, Inc.
 *
 * SPDX-License-Identifier: Apache-2.0
 */


#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/sys/printk.h>
#include <zephyr/sys/util.h>
#include <zephyr/irq.h>
#include <stdbool.h>


#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/drivers/i2s.h>
#include <zephyr/sys/iterable_sections.h>
#include <zephyr/drivers/clock_control.h>
#include <zephyr/drivers/clock_control/nrf_clock_control.h>
#include <zephyr/logging/log.h>
#include "voice_smoke.h"
#include "voice_visitor.h"
#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/devicetree.h>
#include <zephyr/drivers/i2c.h>
#include <zephyr/sys/printk.h>


#define LOG_MODULE_NAME i2s_test
LOG_MODULE_REGISTER(LOG_MODULE_NAME);

#define SAMPLE_NO 	(48)
#define NUM_BLOCKS 	(4)
#define NUM_CHANNEL	(1)
#define BLOCK_SIZE 	(SAMPLE_NO * NUM_CHANNEL * sizeof(int16_t))

K_MEM_SLAB_DEFINE(tx_mem_slab, BLOCK_SIZE, NUM_BLOCKS, 4);

const struct device *const i2s_dev = DEVICE_DT_GET(DT_ALIAS(i2s_node0));

#define NUM_VOICE 	(2)
static uint8_t voice_index  = 0;
static size_t signal_offset = 0;

typedef struct {
    const unsigned int    voice_length;
    const unsigned char*   voice_data;
} voice_data_t;

static voice_data_t voice_table[NUM_VOICE] = {
	// {
	// 	.voice_length = __48kHz16bit_mono_voice_besokare_pcm_len,
	// 	.voice_data   = __48kHz16bit_mono_voice_besokare_pcm,
	// },

	// {
	// 	.voice_length = __48kHz16bit_mono_voice_handy_pcm_len,
	// 	.voice_data   = __48kHz16bit_mono_voice_handy_pcm,
	// },

	// {
	// 	.voice_length = __48kHz16bit_mono_voice_mobiel_pcm_len,
	// 	.voice_data   = __48kHz16bit_mono_voice_mobiel_pcm,
	// },

	{
		.voice_length = __48kHz16bit_mono_voice_smoke_pcm_len,
		.voice_data   = __48kHz16bit_mono_voice_smoke_pcm,
	},

	{
		.voice_length = __48kHz16bit_mono_voice_visitor_pcm_len,
		.voice_data   = __48kHz16bit_mono_voice_visitor_pcm,
	},
};


static int configure_stream(const struct device *dev_i2s, uint32_t frame_clk_freq)
{
	int err;
	struct i2s_config i2s_cfg;

	i2s_cfg.word_size = 16U;

	i2s_cfg.channels = NUM_CHANNEL;

	i2s_cfg.format = I2S_FMT_DATA_FORMAT_I2S;
	i2s_cfg.frame_clk_freq = frame_clk_freq;
	i2s_cfg.block_size = BLOCK_SIZE;
	i2s_cfg.timeout = 1000;
	i2s_cfg.options = I2S_OPT_FRAME_CLK_MASTER | I2S_OPT_BIT_CLK_MASTER;

	i2s_cfg.mem_slab = &tx_mem_slab;
	err = i2s_configure(dev_i2s, I2S_DIR_TX, &i2s_cfg);
	if (err < 0) {
		LOG_ERR("Failed to configure I2S TX stream (%d)\n",
			 err);
		return err;
	}

	return 0;
}



static void fill_buf_slave(int16_t *tx_block)
{
    // read data, change to stereo
    for (int i = 0; i < SAMPLE_NO; i++) {
        int16_t sample;
        
        if (signal_offset >= voice_table[voice_index].voice_length / sizeof(int16_t)) {
            signal_offset = 0;
        }
        
        sample = ((int16_t*)(voice_table[voice_index].voice_data))[signal_offset++];

		float f_sample = (float)(sample) * 0.5f; // Adjust volume based on volume_scale
		int16_t i_sample = (int16_t)(f_sample);

        // // stereo data：L, R, L, R...
        // tx_block[i * 2] = sample;      // Left
        // tx_block[i * 2 + 1] = sample;  // Right

		// mono data：L, L, L, L...
		tx_block[i] = i_sample;      // Left
		// tx_block[i] = sample;      // Left
    }
}


int main(void)
{
	int err;
	int tx_idx;
	void *tx_block[NUM_BLOCKS];

	LOG_INF("The audio demo compiled at: %s.\n", COMPILE_TIME);

	LOG_INF("I2S Sample started");


    if (!device_is_ready(i2s_dev)) {
		LOG_ERR("Device %s is not ready.", i2s_dev->name);
		return -EIO;
	}

	
	err = configure_stream(i2s_dev, 48000);

	if(err) {
		LOG_ERR("configure_stream() failed. (err %d)", err);
	}

	/* Prepare TX data blocks */
	for (tx_idx = 0; tx_idx < NUM_BLOCKS; tx_idx++) {
		err = k_mem_slab_alloc(&tx_mem_slab, &tx_block[tx_idx], K_FOREVER);
		if(err) {
			LOG_ERR("k_mem_slab_alloc() failed. (err %d)", err);
			return err;
		}
		fill_buf_slave(tx_block[tx_idx]);
	}

	tx_idx = 0;

	/* Prefill TX queue */
	err = i2s_write(i2s_dev, tx_block[tx_idx++], BLOCK_SIZE);
	if(err) {
		LOG_ERR("i2s write() failed. (err %d)", err);
		return err;
	}

	err = i2s_write(i2s_dev, tx_block[tx_idx++], BLOCK_SIZE);
	if(err) {
		LOG_ERR("i2s write() failed. (err %d)", err);
		return err;
	}

	err = i2s_trigger(i2s_dev, I2S_DIR_TX, I2S_TRIGGER_START);
    if (err) {
        LOG_ERR("i2s_trigger failed: %d", err);
        return err;
    }

    LOG_INF("Audio started");

	/* Keep alternating between the two buffers */
	while (1) {
		
		tx_idx = tx_idx & (NUM_BLOCKS-1);

		fill_buf_slave(tx_block[tx_idx]);
		
		err = i2s_write(i2s_dev, tx_block[tx_idx], BLOCK_SIZE);
		if(err) {
			LOG_ERR("i2s write() failed. (err %d)", err);
			
			k_mem_slab_free(&tx_mem_slab, tx_block[tx_idx]);
            err = k_mem_slab_alloc(&tx_mem_slab, &tx_block[tx_idx], K_FOREVER);
            if (err) {
				LOG_ERR("k_mem_slab_alloc() failed. (err %d)", err);
				continue;
			}

            fill_buf_slave(tx_block[tx_idx]);
            err = i2s_write(i2s_dev, tx_block[tx_idx], BLOCK_SIZE);
			if(err) {
				LOG_ERR("i2s write() failed. (err %d)", err);
				continue;
			}
		}

		tx_idx++;
	}

	return 0;
}
