// audio_demo.cpp
#include <iostream>
#include <vector>
#include <string>
#include <thread>
#include <chrono>
#include <signal.h>
#include <atomic>
#include <k230_audio.h>
#include <k230_audio.h>
#include <k230_wave.h>
#include "kws.h"

// Configuration parameters for audio input
int get_chunk = 16000 * 0.3;         // Number of samples per audio chunk (approx. 300ms)
AudioFormat get_format = paInt16;    // Audio format: 16-bit PCM
int get_channels = 1;                // Mono input
int get_samplerate = 16000;          // Sampling rate: 16 kHz
AudioStream* input_stream = nullptr; // Pointer to input audio stream
ArduinoAudio input_audio;            // Audio input handler

// Keyword spotting model configuration
std::string kmodel_path = "kws.kmodel"; // Path to KWS (Keyword Spotting) model file
float thresh = 0.5;                     // Confidence threshold for keyword detection
int debug_mode = 1;                     // Debug mode flag (1 = enabled)

// Configuration parameters for audio output (playback)
AudioStream* output_stream = nullptr;   // Pointer to output audio stream
std::unique_ptr<WaveRead> wf = nullptr; // Smart pointer to WAV file reader
ArduinoAudio output_audio;              // Audio output handler
int play_channels = 1;                  // Mono output
int play_sampwidth = 2;                 // Sample width (bytes per sample)
int play_samplerate = 16000;            // Output sampling rate: 16 kHz
int play_chunk = play_samplerate / 25;  // Playback chunk size (40ms of audio)

// ---------------------------------------------------------------------------
// Function: play_wav
// Description: Plays a WAV audio file through the output audio stream.
// Parameters:
//   - filename: Path to the WAV file to be played.
// ---------------------------------------------------------------------------
void play_wav(const std::string& filename) {
    try {
        // Open the WAV file for reading
        wf = open_read(filename);
        play_channels = wf->get_channels();
        play_sampwidth = wf->get_sampwidth();
        play_samplerate = wf->get_framerate();
        play_chunk = play_samplerate / 25;  // Update chunk size based on sample rate

        // Determine playback format based on sample width
        AudioFormat play_format = static_cast<AudioFormat>(output_audio.get_format_from_width(play_sampwidth));

        // Initialize output audio stream
        output_stream = output_audio.open(play_samplerate, play_channels, play_format,
                                          false, true, -1, -1, true, play_chunk, false);
        output_stream->volume(85);      // Set output volume (0–100)
        output_stream->start_stream();  // Start playback
        std::cout << "Start playing" << std::endl;

        // Read and write audio data in chunks
        auto data = wf->read_frames(play_chunk);
        while (!data.empty()) {
            std::vector<uint8_t> audio_data(data.begin(), data.end());
            output_stream->write(audio_data);   // Send data to output stream
            data = wf->read_frames(play_chunk); // Read next chunk
        }

        // Small delay to ensure all data is played
        delay(200);

        // Stop and close the audio stream
        output_stream->stop_stream();
        output_audio.close(output_stream);
        wf->close();

        std::cout << "Playback finished" << std::endl;
    } catch (const std::exception& e) {
        std::cerr << "Error in play_audio: " << e.what() << std::endl;
    }
}

// ---------------------------------------------------------------------------
// Function: keyword_spotting
// Description: Performs real-time keyword spotting (KWS) using microphone input.
//              When a keyword is detected, a WAV file is played back.
// ---------------------------------------------------------------------------
static void keyword_spotting() {
    try {
        // Initialize keyword spotting model
        KWS kws(kmodel_path, thresh, debug_mode);

        // Open input audio stream for recording
        input_stream = input_audio.open(get_samplerate, get_channels, get_format,
                                        true, false, -1, -1, true, get_chunk, false);
        input_stream->volume(70, LEFT);               // Set microphone gain
        input_stream->enable_audio3a(AUDIO_3A_ENABLE_ANS); // Enable noise suppression
        input_stream->start_stream();                 // Start recording

        // Number of samples per frame for KWS processing
        size_t num_samples = 4800;
        std::vector<float> frame(num_samples);        // Buffer for audio samples

        // Continuous processing loop
        while (true) {
            std::vector<uint8_t> data;
            if (input_stream->read(data, 0, true) == 1) {
                continue;
            }

            // Ensure we have enough samples for processing
            if (data.size() < num_samples * 2) {
                continue;
            }

            // Convert raw PCM data (int16) to float samples
            const int16_t* pcm = reinterpret_cast<const int16_t*>(data.data());
            for (size_t i = 0; i < num_samples; ++i) {
                frame[i] = static_cast<float>(pcm[i]);
            }

            // Run keyword spotting pipeline
            kws.pre_process(frame);  // Feature extraction
            kws.inference();         // Neural network inference
            int res = kws.post_process(); // Post-processing and keyword detection

            // If keyword detected, play a response sound
            if (res == 1) {
                play_wav("./wozai.wav");
            }
        }

        // Stop input stream and release resources
        input_stream->stop_stream();
        input_audio.close(input_stream);

    } catch (const std::exception& e) {
        std::cerr << "Error in keyword_spotting: " << e.what() << std::endl;
    }
}

// ---------------------------------------------------------------------------
// Function: setup
// Description: Initializes the keyword spotting system.
// ---------------------------------------------------------------------------
void setup()
{
    keyword_spotting();  // Start keyword spotting process
}

// ---------------------------------------------------------------------------
// Function: loop
// Description: Main loop (not used in this implementation).
// ---------------------------------------------------------------------------
void loop()
{

}
