/*
 * Copyright (c) 2023 Nordic Semiconductor ASA
 *
 * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
 */

#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
#include <zephyr/zbus/zbus.h>
#include <zephyr/sys/byteorder.h>
#include <zephyr/console/console.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include "nrf5340_audio_dk.h"
#include "led.h"
#include "button_assignments.h"
#include "macros_common.h"
#include "audio_system.h"
#include "bt_mgmt.h"
#include "fw_info_app.h"
#include "sd_card.h"
#include "sd_card_playback.h"
#include "button_handler.h"
#include "zbus_common.h"

LOG_MODULE_REGISTER(main, CONFIG_MAIN_LOG_LEVEL);

/* ZBus subscribers */
ZBUS_SUBSCRIBER_DEFINE(button_evt_sub, CONFIG_BUTTON_MSG_SUB_QUEUE_SIZE);

/* ZBus channels */
ZBUS_CHAN_DECLARE(button_chan);

/* Thread definitions */
static struct k_thread button_msg_sub_thread_data;
static struct k_thread console_thread_data;
static k_tid_t button_msg_sub_thread_id;
static k_tid_t console_thread_id;

K_THREAD_STACK_DEFINE(button_msg_sub_thread_stack, CONFIG_BUTTON_MSG_SUB_STACK_SIZE);
K_THREAD_STACK_DEFINE(console_thread_stack, 1024);

/* SD Card file list buffer */
#define MAX_FILES 50
#define MAX_FILENAME_LENGTH 64
static char file_list[MAX_FILES][MAX_FILENAME_LENGTH];
static int file_count = 0;

/* Console command buffer */
#define CONSOLE_CMD_MAX_LEN 128
static char console_cmd_buffer[CONSOLE_CMD_MAX_LEN];

/* Function prototypes */
static void button_msg_sub_thread(void);
static void console_thread(void);
static int zbus_subscribers_create(void);
static int zbus_link_producers_observers(void);
static void list_sd_card_files(void);
static void play_audio_file(const char *filename);
static void show_help(void);
static void process_console_command(char *cmd);

/**
 * @brief Button message subscriber thread
 */
static void button_msg_sub_thread(void)
{
	const struct zbus_channel *chan;

	while (1) {
		if (zbus_sub_wait(&button_evt_sub, &chan, K_FOREVER)) {
			LOG_ERR("Failed to wait for zbus message");
			continue;
		}

		const struct button_msg *msg = zbus_chan_const_msg(chan);

		if (msg == NULL) {
			LOG_ERR("Failed to get message from channel");
			continue;
		}

		LOG_DBG("Button event: pin=%d, action=%d", msg->button_pin, msg->button_action);

		switch (msg->button_pin) {
		case BUTTON_VOLUME_DOWN:
			if (msg->button_action == BUTTON_PRESS) {
				LOG_INF("Volume Down button pressed");
			}
			break;

		case BUTTON_VOLUME_UP:
			if (msg->button_action == BUTTON_PRESS) {
				LOG_INF("Volume Up button pressed");
			}
			break;

		case BUTTON_PLAY_PAUSE:
			if (msg->button_action == BUTTON_PRESS) {
				LOG_INF("Play/Pause button pressed");
			}
			break;

		case BUTTON_4:
			if (msg->button_action == BUTTON_PRESS) {
				LOG_INF("Button 4 pressed - List SD card files");
				list_sd_card_files();
			}
			break;

		case BUTTON_5:
			if (msg->button_action == BUTTON_PRESS) {
				LOG_INF("Button 5 pressed");
			}
			break;

		default:
			LOG_DBG("Unhandled button: %d", msg->button_pin);
			break;
		}
	}
}

/**
 * @brief Console thread for handling serial commands
 */
static void console_thread(void)
{
	console_getline_init();

	while (1) {
		printf("\nSD Card Audio Player> ");
		fflush(stdout);

		if (console_getline(console_cmd_buffer, CONSOLE_CMD_MAX_LEN) > 0) {
			process_console_command(console_cmd_buffer);
		}

		k_sleep(K_MSEC(100));
	}
}

/**
 * @brief Process console commands
 */
static void process_console_command(char *cmd)
{
	char *token;
	char *saveptr;

	// Remove newline
	cmd[strcspn(cmd, "\n")] = 0;

	token = strtok_r(cmd, " ", &saveptr);
	if (token == NULL) {
		return;
	}

	if (strcmp(token, "help") == 0 || strcmp(token, "h") == 0) {
		show_help();
	}
	else if (strcmp(token, "list") == 0 || strcmp(token, "ls") == 0) {
		list_sd_card_files();
	}
	else if (strcmp(token, "playback") == 0 || strcmp(token, "p") == 0) {
		token = strtok_r(NULL, " ", &saveptr);
		if (token == NULL) {
			printf("Usage: playback <filename>\n");
			printf("Example: playback audio.wav\n");
		} else {
			play_audio_file(token);
		}
	}
	else if (strcmp(token, "status") == 0 || strcmp(token, "s") == 0) {
		if (sd_card_playback_is_active()) {
			printf("Playback is active\n");
		} else {
			printf("No playback active\n");
		}
	}
	else if (strcmp(token, "clear") == 0 || strcmp(token, "cls") == 0) {
		printf("\033[2J\033[H"); // Clear screen
	}
	else {
		printf("Unknown command: %s\n", token);
		printf("Type 'help' for available commands\n");
	}
}

/**
 * @brief Show help information
 */
static void show_help(void)
{
	printf("\n=== SD Card Audio Player Commands ===\n");
	printf("help, h                    - Show this help\n");
	printf("list, ls                   - List all audio files on SD card\n");
	printf("playback <filename>, p     - Play audio file\n");
	printf("status, s                  - Show current playback status\n");
	printf("clear, cls                 - Clear screen\n");
	printf("=====================================\n\n");
}

/**
 * @brief List all audio files on SD card
 */
static void list_sd_card_files(void)
{
	printf("Scanning SD card for audio files...\n");

	// Clear previous file list
	memset(file_list, 0, sizeof(file_list));
	file_count = 0;

	// Search for WAV files
	int wav_count = sd_card_list_files_match(MAX_FILES, MAX_FILENAME_LENGTH,
						file_list, NULL, "*.wav");
	if (wav_count > 0) {
		file_count = wav_count;
		printf("Found %d WAV files:\n", wav_count);
		for (int i = 0; i < wav_count; i++) {
			printf("  %d: %s\n", i + 1, file_list[i]);
		}
	}

	// Search for LC3 files
	int lc3_count = sd_card_list_files_match(MAX_FILES - file_count, MAX_FILENAME_LENGTH,
						&file_list[file_count], NULL, "*.lc3");
	if (lc3_count > 0) {
		printf("Found %d LC3 files:\n", lc3_count);
		for (int i = 0; i < lc3_count; i++) {
			printf("  %d: %s\n", file_count + i + 1, file_list[file_count + i]);
		}
		file_count += lc3_count;
	}

	if (file_count == 0) {
		printf("No audio files found on SD card\n");
	} else {
		printf("Total audio files found: %d\n", file_count);
	}
}

/**
 * @brief Play audio file
 */
static void play_audio_file(const char *filename)
{
	if (filename == NULL || strlen(filename) == 0) {
		printf("Error: Invalid filename\n");
		return;
	}

	printf("Playing file: %s\n", filename);

	// Check if it's a WAV file
	if (strstr(filename, ".wav") != NULL) {
		int ret = sd_card_playback_wav((char *)filename);
		if (ret == 0) {
			printf("Started playing WAV file: %s\n", filename);
		} else {
			printf("Failed to play WAV file %s: %d\n", filename, ret);
		}
	}
	// Check if it's an LC3 file
	else if (strstr(filename, ".lc3") != NULL) {
		int ret = sd_card_playback_lc3((char *)filename);
		if (ret == 0) {
			printf("Started playing LC3 file: %s\n", filename);
		} else {
			printf("Failed to play LC3 file %s: %d\n", filename, ret);
		}
	}
	else {
		printf("Unsupported file format: %s\n", filename);
		printf("Supported formats: .wav, .lc3\n");
	}
}

/**
 * @brief Create ZBus subscribers
 */
static int zbus_subscribers_create(void)
{
	int ret;

	button_msg_sub_thread_id = k_thread_create(&button_msg_sub_thread_data,
						 button_msg_sub_thread_stack,
						 K_THREAD_STACK_SIZEOF(button_msg_sub_thread_stack),
						 button_msg_sub_thread, NULL, NULL, NULL,
						 K_PRIO_PREEMPT(CONFIG_BUTTON_MSG_SUB_PRIO), 0,
						 K_NO_WAIT);

	if (!button_msg_sub_thread_id) {
		LOG_ERR("Failed to create button message subscriber thread");
		return -ENOMEM;
	}

	ret = k_thread_name_set(button_msg_sub_thread_id, "BUTTON_MSG_SUB");
	if (ret) {
		LOG_ERR("Failed to set thread name");
		return ret;
	}

	// Create console thread
	console_thread_id = k_thread_create(&console_thread_data,
					   console_thread_stack,
					   K_THREAD_STACK_SIZEOF(console_thread_stack),
					   console_thread, NULL, NULL, NULL,
					   K_PRIO_PREEMPT(CONFIG_BUTTON_MSG_SUB_PRIO), 0,
					   K_NO_WAIT);

	if (!console_thread_id) {
		LOG_ERR("Failed to create console thread");
		return -ENOMEM;
	}

	ret = k_thread_name_set(console_thread_id, "CONSOLE_THREAD");
	if (ret) {
		LOG_ERR("Failed to set console thread name");
		return ret;
	}

	ret = zbus_chan_add_obs(&button_chan, &button_evt_sub, K_NO_WAIT);
	if (ret) {
		LOG_ERR("Failed to add button subscriber to channel");
		return ret;
	}

	return 0;
}

/**
 * @brief Link ZBus producers and observers
 */
static int zbus_link_producers_observers(void)
{
	int ret;

	ret = zbus_chan_add_obs(&button_chan, &button_evt_sub, K_NO_WAIT);
	if (ret) {
		LOG_ERR("Failed to add button subscriber to channel");
		return ret;
	}

	return 0;
}

/**
 * @brief Main function
 */
int main(void)
{
	int ret;

	printf("\n=== nRF5340 Audio DK SD Card Playback Application ===\n");
	printf("Serial Monitor Commands:\n");
	printf("  list, ls              - List SD card files\n");
	printf("  playback <filename>   - Play audio file\n");
	printf("  status, s             - Show playback status\n");
	printf("  help, h               - Show help\n");
	printf("  clear, cls            - Clear screen\n");
	printf("====================================================\n\n");

	/* Initialize board */
	ret = nrf5340_audio_dk_init();
	if (ret) {
		LOG_ERR("Failed to initialize nRF5340 Audio DK: %d", ret);
		return ret;
	}

	/* Initialize button handler */
	ret = button_handler_init();
	if (ret) {
		LOG_ERR("Failed to initialize button handler: %d", ret);
		return ret;
	}

	/* Initialize SD card */
	ret = sd_card_init();
	if (ret) {
		LOG_ERR("Failed to initialize SD card: %d", ret);
		printf("SD card may not be inserted or properly connected\n");
	} else {
		printf("SD card initialized successfully\n");
	}

	/* Initialize SD card playback */
	ret = sd_card_playback_init();
	if (ret) {
		LOG_ERR("Failed to initialize SD card playback: %d", ret);
		return ret;
	}
	printf("SD card playback initialized successfully\n");

	/* Initialize audio system */
	ret = audio_system_init();
	if (ret) {
		LOG_ERR("Failed to initialize audio system: %d", ret);
		return ret;
	}

	/* Initialize Bluetooth management */
	ret = bt_mgmt_init();
	if (ret) {
		LOG_ERR("Failed to initialize Bluetooth management: %d", ret);
		return ret;
	}

	/* Create ZBus subscribers */
	ret = zbus_subscribers_create();
	if (ret) {
		LOG_ERR("Failed to create ZBus subscribers: %d", ret);
		return ret;
	}

	/* Link ZBus producers and observers */
	ret = zbus_link_producers_observers();
	if (ret) {
		LOG_ERR("Failed to link ZBus producers and observers: %d", ret);
		return ret;
	}

	/* Start threads */
	k_thread_start(button_msg_sub_thread_id);
	k_thread_start(console_thread_id);

	printf("Application started successfully\n");
	printf("Type 'help' for available commands\n");

	/* Main loop */
	while (1) {
		k_sleep(K_SECONDS(1));
	}

	return 0;
}
