#include <zephyr/kernel.h>
#include <zephyr/audio/dmic.h>
#include <zephyr/drivers/i2s.h>
#include <zephyr/drivers/i2c.h>
#include <zephyr/logging/log.h>
#include "tas.h"

#define MAX_SAMPLE_RATE 16000
#define SAMPLE_BIT_WIDTH 16
#define BYTES_PER_SAMPLE sizeof(int16_t)
#define READ_TIMEOUT 1000
#define SAMPLES_PER_BLOCK 160
#define NUMBER_OF_CHANNELS 1
#define I2S_TX_NODE DT_NODELABEL(i2s_rxtx)
#define I2C1_NODE DT_NODELABEL(tas2563)
#define BLOCK_SIZE (BYTES_PER_SAMPLE * SAMPLES_PER_BLOCK)
#define MAX_BLOCK_SIZE BLOCK_SIZE
#define BLOCK_COUNT 16

LOG_MODULE_REGISTER(dmic_sample);
K_MEM_SLAB_DEFINE_STATIC(mem_slab, MAX_BLOCK_SIZE, BLOCK_COUNT, 4);
static const struct i2c_dt_spec dev_i2c = I2C_DT_SPEC_GET(I2C1_NODE);

static void i2cWrite(uint8_t val1, uint8_t val2)
{
	uint8_t i2cConfig[2] = {val1, val2};
	uint8_t ret = i2c_write_dt(&dev_i2c, i2cConfig, sizeof(i2cConfig));
	if (ret != 0)
	{
		printk("Failed to write to I2C device address %x at reg. %x \n\r", dev_i2c.addr, i2cConfig[0]);
		printk("%d", ret);
	}
	else
	{
		printk("write success val1=%0.02x,val2=%0.02x \n", val1, val2);
	}
}

static void i2cWriteRead(uint8_t val1)
{
	uint8_t regs[1] = {val1};
	printk("val1 : %d\n", regs[0]);
	uint8_t reading[1] = {0};
	int ret = i2c_write_read_dt(&dev_i2c, &regs[0], 1, &reading[0], 1);
	if (ret != 0)
	{
		printk("Failed to write/read I2C device address %x at Reg. %x \n\r", dev_i2c.addr, regs[0]);
	}
	else
	{
		printk("read write success. %x\n", reading[0]);
	}
}
static void playbackInit(void)
{
	i2cWrite(PAGE, PAGE_SET0);
	i2cWrite(BOOK, BOOK_SET);
	i2cWrite(SW_RESET, SWRE_SET);
	k_msleep(10);

	i2cWrite(PB_CFG1, 0X02U);
	i2cWrite(TDM_CFG0, TDM_CFG0_SET);
	i2cWrite(TDM_CFG1, 0x02U);
	i2cWrite(TDM_CFG2, 0x30U);
	i2cWrite(PWR_CTL, PWR_CTL_RESET);
	i2cWriteRead(PB_CFG1);

	i2cWriteRead(PWR_CTL);
	i2cWriteRead(INT_LTCH0);
}

static bool configure_streams(const struct device *i2s_dev_tx, const struct i2s_config *config)
{
	int ret = i2s_configure(i2s_dev_tx, I2S_DIR_TX, config);
	if (ret == 0)
	{
		printk("Configure Stream Successful.\n");
		return true;
	}
	printk("Failed to configure streams: %d\n", ret);
	return false;
}
static bool prepare_transfer(const struct device *i2s_dev_tx)
{
	int ret;

	for (int i = 0; i < 2; ++i)
	{
		void *mem_block;

		ret = k_mem_slab_alloc(&mem_slab, &mem_block, K_NO_WAIT);
		if (ret < 0)
		{
			printk("Failed to allocate TX block %d: %d\n", i, ret);
			return false;
		}
		memset(mem_block, 0, BLOCK_SIZE);

		ret = i2s_write(i2s_dev_tx, mem_block, BLOCK_SIZE);
		if (ret < 0)
		{
			printk("Failed to write block %d: %d\n", i, ret);
			return false;
		}
	}

	return true;
}
static bool trigger_command(const struct device *i2s_dev_tx, enum i2s_trigger_cmd cmd)
{
	int ret = i2s_trigger(i2s_dev_tx, I2S_DIR_TX, cmd);
	if (ret == 0)
	{
		printk("Trigger Command Successful.\n");
		return true;
	}
	printk("Failed to trigger command %d: %d\n", cmd, ret);
	return false;
}
int main(void)
{
	const struct device *const dmic_dev = DEVICE_DT_GET(DT_NODELABEL(dmic_dev));
	const struct device *const i2s_dev_tx = DEVICE_DT_GET(I2S_TX_NODE);
	struct i2s_config config;
	int ret;

	LOG_INF("DMIC sample");

	if (!device_is_ready(dmic_dev))
	{
		LOG_ERR("%s is not ready", dmic_dev->name);
		return 0;
	}

	if (!device_is_ready(i2s_dev_tx))
	{
		printk("%s is not ready\n", i2s_dev_tx->name);
		return 0;
	}

	struct pcm_stream_cfg stream = {
		.pcm_width = SAMPLE_BIT_WIDTH,
		.mem_slab = &mem_slab,
	};
	struct dmic_cfg cfg = {
		.io = {
			.min_pdm_clk_freq = 1000000,
			.max_pdm_clk_freq = 3500000,
			.min_pdm_clk_dc = 40,
			.max_pdm_clk_dc = 60,
		},
		.streams = &stream,
		.channel = {
			.req_num_streams = 1,
		},
	};

	cfg.channel.req_num_chan = 1;
	cfg.channel.req_chan_map_lo = dmic_build_channel_map(0, 0, PDM_CHAN_LEFT);
	cfg.streams[0].pcm_rate = MAX_SAMPLE_RATE;
	cfg.streams[0].block_size = BLOCK_SIZE;

	ret = dmic_configure(dmic_dev, &cfg);
	if (ret < 0)
	{
		LOG_ERR("Failed to configure the driver: %d", ret);
		return ret;
	}

	config.word_size = 16;
	config.channels = NUMBER_OF_CHANNELS;
	config.format = I2S_FMT_DATA_FORMAT_I2S;
	config.options = I2S_OPT_BIT_CLK_MASTER | I2S_OPT_FRAME_CLK_MASTER;
	config.frame_clk_freq = MAX_SAMPLE_RATE;
	config.mem_slab = &mem_slab;
	config.block_size = BLOCK_SIZE;
	config.timeout = READ_TIMEOUT;
	if (!configure_streams(i2s_dev_tx, &config))
	{
		return 0;
	}
	ret = dmic_trigger(dmic_dev, DMIC_TRIGGER_START);
	if (ret < 0)
	{
		LOG_ERR("START trigger failed: %d", ret);
		return ret;
	}
	if (!prepare_transfer(i2s_dev_tx))
	{
		return 0;
	}
	if (!trigger_command(i2s_dev_tx, I2S_TRIGGER_START))
	{
		return 0;
	}

	while (1)
	{
		void *buffer;
		uint32_t size;
		ret = dmic_read(dmic_dev, 0, &buffer, &size, READ_TIMEOUT);
		if (ret < 0)
		{
			LOG_ERR("read failed: %d", ret);
			return ret;
		}

		ret = i2s_write(i2s_dev_tx, buffer, size);
		if (ret)
		{
			LOG_ERR("Failed to write I2S buffer: %d\n", ret);
			k_mem_slab_free(&mem_slab, buffer);
			return;
		}
		// // LOG_INF("%d samples", num_samples);

		LOG_INF("%d - got buffer of %u bytes", buffer, size);
	}

	ret = dmic_trigger(dmic_dev, DMIC_TRIGGER_STOP);
	if (ret < 0)
	{
		LOG_ERR("STOP trigger failed: %d", ret);
		return ret;
	}
	else
	{
		printk("success\n");
	}

	LOG_INF("Exiting");
	return 0;
}
