#ifndef K230_AUDIO_H
#define K230_AUDIO_H

#include <cstdint>
#include <string>
#include <vector>
#include <memory>
#include <functional>
#include "k_audio_comm.h"
#include "k_payload_comm.h"
#include "k_ai_comm.h"
#include "k_ao_comm.h"
#include "k_vb_comm.h"

/**
 * @brief Enumeration of supported audio formats
 *
 * Represents different PCM sample formats with varying bit depths
 */
enum AudioFormat{
    paInt16 = 0,   ///< 16-bit signed integer samples
    paInt24 = 1,   ///< 24-bit signed integer samples
    paInt32 = 2    ///< 32-bit signed integer samples
};

/**
 * @brief Enumeration of audio channels
 *
 * Used for volume control and channel manipulation
 */
enum Channel{
    LEFT = 1,       ///< Left audio channel
    RIGHT = 2,      ///< Right audio channel
    LEFT_RIGHT = 3  ///< Both left and right channels
};

/**
 * @brief Enumeration of audio 3A (Acoustic Echo Cancellation,
 *        Automatic Gain Control, Active Noise Suppression) features
 *
 * Bitmask values that can be combined to enable multiple features
 */
enum Audio3AEnable{
    AUDIO_3A_ENABLE_NONE = 0,  ///< No 3A features enabled
    AUDIO_3A_ENABLE_ANS = 1,   ///< Enable Active Noise Suppression
    AUDIO_3A_ENABLE_AGC = 2,   ///< Enable Automatic Gain Control
    AUDIO_3A_ENABLE_AEC = 4    ///< Enable Acoustic Echo Cancellation
};

/**
 * @brief Enumeration of audio device types
 */
enum DeviceType {
    DEVICE_I2S = 0,  ///< I2S audio device
    DEVICE_PDM = 1   ///< PDM audio device
};

/**
 * @brief Base class for audio streams
 *
 * Provides a common interface for both input and output audio streams,
 * handling basic audio parameters and stream control.
 */
class AudioStream {
public:
    /**
     * @brief Construct a new Audio Stream object
     *
     * @param rate Sample rate in Hz
     * @param channels Number of audio channels
     * @param format Audio sample format
     * @param input True if this is an input stream
     * @param output True if this is an output stream
     * @param input_device_index Index of input device to use (-1 for default)
     * @param output_device_index Index of output device to use (-1 for default)
     * @param enable_codec True to enable hardware codec
     * @param frames_per_buffer Number of frames per buffer
     * @param start True to start the stream immediately
     */
    AudioStream(int rate, int channels, AudioFormat format, bool input, bool output,
           int input_device_index, int output_device_index, bool enable_codec,
           int frames_per_buffer, bool start);

    /**
     * @brief Destroy the Audio Stream object
     */
    virtual ~AudioStream() = default;

    /**
     * @brief Start the audio stream
     */
    virtual void start_stream() = 0;

    /**
     * @brief Stop the audio stream
     */
    virtual void stop_stream() = 0;

    /**
     * @brief Read audio data from the stream
     *
     * @param data Buffer to store read data
     * @param chn Channel index to read from (default 0)
     * @param block True to block until data is available
     * @return Number of bytes read
     */
    virtual int read(std::vector<uint8_t>& data, int chn = 0, bool block = true) = 0;

    /**
     * @brief Write audio data to the stream
     *
     * @param data Buffer containing data to write
     * @return Number of bytes written
     */
    virtual int write(const std::vector<uint8_t>& data) = 0;

    /**
     * @brief Close the audio stream
     */
    virtual void close() = 0;

    /**
     * @brief Set or get the volume level
     *
     * @param vol Volume level (0-100, -1 to get current volume)
     * @param channel Channel(s) to adjust
     * @return Current volume level after adjustment
     */
    virtual int volume(int vol = -1, Channel channel = LEFT_RIGHT) = 0;

    /**
     * @brief Swap left and right audio channels
     *
     * @return 0 on success, non-zero on error
     */
    virtual int swap_left_right() = 0;

    /**
     * @brief Enable/disable audio 3A features
     *
     * @param audio3a_value Bitmask of Audio3AEnable values
     * @return 0 on success, non-zero on error
     */
    virtual int enable_audio3a(int audio3a_value) = 0;

    /**
     * @brief Send far-end echo frame for AEC processing
     *
     * @param frame_data Buffer containing echo frame
     * @param data_len Length of frame data in bytes
     * @return 0 on success, non-zero on error
     */
    virtual int audio3a_send_far_echo_frame(const std::vector<uint8_t>& frame_data, int data_len) = 0;

    /**
     * @brief Check if the stream is running
     *
     * @return true if stream is running
     * @return false otherwise
     */
    bool is_running() const { return _is_running; }

    /**
     * @brief Check if this is an input stream
     *
     * @return true if input stream
     * @return false otherwise
     */
    bool is_input() const { return _is_input; }

    /**
     * @brief Check if this is an output stream
     *
     * @return true if output stream
     * @return false otherwise
     */
    bool is_output() const { return _is_output; }

    /**
     * @brief Get the sample rate
     *
     * @return Sample rate in Hz
     */
    int get_rate() const { return _rate; }

    /**
     * @brief Get the number of channels
     *
     * @return Number of channels
     */
    int get_channels() const { return _channels; }

    /**
     * @brief Get the audio format
     *
     * @return Audio format as AudioFormat enum
     */
    int get_format() const { return _format; }

protected:
    bool _is_input;               ///< True if this is an input stream
    bool _is_output;              ///< True if this is an output stream
    bool _is_running;             ///< True if stream is active
    int _rate;                    ///< Sample rate in Hz
    int _channels;                ///< Number of audio channels
    AudioFormat _format;          ///< Audio sample format
    int _frames_per_buffer;       ///< Number of frames per buffer
    int _input_device_index;      ///< Input device index
    int _output_device_index;     ///< Output device index
    bool _enable_codec;           ///< True if hardware codec is enabled
    DeviceType _device_type;      ///< Type of audio device
};

/**
 * @brief Audio output stream class
 *
 * Implements audio output functionality for playing audio through speakers
 * or other output devices.
 */
class WriteStream : public AudioStream {
public:
    /**
     * @brief Construct a new Write Stream object
     *
     * @param rate Sample rate in Hz
     * @param channels Number of audio channels
     * @param format Audio sample format
     * @param input True if this is an input stream (should be false)
     * @param output True if this is an output stream (should be true)
     * @param input_device_index Input device index (unused)
     * @param output_device_index Output device index (-1 for default)
     * @param enable_codec True to enable hardware codec
     * @param frames_per_buffer Number of frames per buffer
     * @param start True to start the stream immediately
     */
    WriteStream(int rate, int channels, AudioFormat format, bool input, bool output,
                int input_device_index, int output_device_index, bool enable_codec,
                int frames_per_buffer, bool start);

    /**
     * @brief Destroy the Write Stream object
     */
    ~WriteStream();

    /**
     * @brief Start the output stream
     */
    void start_stream();

    /**
     * @brief Stop the output stream
     */
    void stop_stream();

    /**
     * @brief Read from output stream (not supported)
     *
     * @param data Buffer to store read data
     * @param chn Channel index
     * @param block Blocking flag
     * @return 0 (not supported)
     */
    int read(std::vector<uint8_t>& data, int chn = 0, bool block = true);

    /**
     * @brief Write audio data to the output stream
     *
     * @param data Buffer containing audio data to write
     * @return Number of bytes written
     */
    int write(const std::vector<uint8_t>& data);

    /**
     * @brief Close the output stream
     */
    void close();

    /**
     * @brief Set or get the volume level
     *
     * @param vol Volume level (0-100, -1 to get current volume)
     * @param channel Channel(s) to adjust
     * @return Current volume level after adjustment
     */
    int volume(int vol = -1, Channel channel = LEFT_RIGHT);

    /**
     * @brief Swap left and right audio channels
     *
     * @return 0 on success, non-zero on error
     */
    int swap_left_right();

    /**
     * @brief Enable/disable audio 3A features (not supported for output)
     *
     * @param audio3a_value Bitmask of Audio3AEnable values
     * @return 0 always
     */
    int enable_audio3a(int audio3a_value) { return 0; } // Not supported for output

    /**
     * @brief Send far-end echo frame (not supported for output)
     *
     * @param frame_data Buffer containing echo frame
     * @param data_len Length of frame data in bytes
     * @return 0 always
     */
    int audio3a_send_far_echo_frame(const std::vector<uint8_t>& frame_data, int data_len) { return 0; } // Not supported

private:
    /**
     * @brief Initialize audio frame structures
     */
    void _init_audio_frame();

    /**
     * @brief Deinitialize audio frame structures
     */
    void _deinit_audio_frame();

    int _ao_dev;                  ///< Audio output device handle
    int _ao_chn;                  ///< Audio output channel
    k_audio_frame* _audio_frame;  ///< Audio frame structure
    k_vb_blk_handle _audio_handle;///< Audio buffer handle
    bool _start_stream;           ///< Flag indicating if stream should start immediately
    k_u32 _pool_id;               ///< Pool ID for audio buffers

    static bool dev_chn_enable[2];///< Static array tracking device channel enable status
};

/**
 * @brief Audio input stream class
 *
 * Implements audio input functionality for recording audio from microphones
 * or other input devices.
 */
class ReadStream : public AudioStream {
public:
    /**
     * @brief Construct a new Read Stream object
     *
     * @param rate Sample rate in Hz
     * @param channels Number of audio channels
     * @param format Audio sample format
     * @param input True if this is an input stream (should be true)
     * @param output True if this is an output stream (should be false)
     * @param input_device_index Input device index (-1 for default)
     * @param output_device_index Output device index (unused)
     * @param enable_codec True to enable audio codec
     * @param frames_per_buffer Number of frames per buffer
     * @param start True to start the stream immediately
     */
    ReadStream(int rate, int channels, AudioFormat format, bool input, bool output,
               int input_device_index, int output_device_index, bool enable_codec,
               int frames_per_buffer, bool start);

    /**
     * @brief Destroy the Read Stream object
     */
    ~ReadStream();

    /**
     * @brief Start the input stream
     */
    void start_stream();

    /**
     * @brief Stop the input stream
     */
    void stop_stream();

    /**
     * @brief Read audio data from the input stream
     *
     * @param data Buffer to store read data
     * @param chn Channel index to read from
     * @param block True to block until data is available
     * @return Number of bytes read
     */
    int read(std::vector<uint8_t>& data, int chn = 0, bool block = true);

    /**
     * @brief Write to input stream (not supported)
     *
     * @param data Buffer containing data to write
     * @return 0 (not supported)
     */
    int write(const std::vector<uint8_t>& data);

    /**
     * @brief Close the input stream
     */
    void close();

    /**
     * @brief Set or get the input volume level
     *
     * @param vol Volume level (0-100, -1 to get current volume)
     * @param channel Channel(s) to adjust
     * @return Current volume level after adjustment
     */
    int volume(int vol = -1, Channel channel = LEFT_RIGHT);

    /**
     * @brief Swap left and right audio channels
     *
     * @return 0 on success, non-zero on error
     */
    int swap_left_right();

    /**
     * @brief Enable/disable audio 3A features
     *
     * @param audio3a_value Bitmask of Audio3AEnable values
     * @return 0 on success, non-zero on error
     */
    int enable_audio3a(int audio3a_value);

    /**
     * @brief Send far-end echo frame for AEC processing
     *
     * @param frame_data Buffer containing echo frame
     * @param data_len Length of frame data in bytes
     * @return 0 on success, non-zero on error
     */
    int audio3a_send_far_echo_frame(const std::vector<uint8_t>& frame_data, int data_len);

private:
    int _ai_dev;                  ///< Audio input device handle
    int _ai_chn;                  ///< Audio input channel
    int _pdm_chncnt;              ///< PDM channel count
    k_audio_frame* _audio_frame;  ///< Audio frame structure
    bool _start_stream;           ///< Flag indicating if stream should start immediately
};

/**
 * @brief Main audio management class
 *
 * Provides initialization, stream creation, and resource management
 * for audio input and output streams.
 */
class ArduinoAudio {
public:
    /**
     * @brief Construct a new Arduino Audio object
     */
    ArduinoAudio();

    /**
     * @brief Destroy the Arduino Audio object
     */
    ~ArduinoAudio();

    /**
     * @brief Configure PDM audio pins
     * @param clk_pin: PDM clock pin number
     * @param data0_pin: PDM data 0 pin number (optional, set to -1 to disable)
     * @param data1_pin: PDM data 1 pin number (optional, set to -1 to disable)
     * @param data2_pin: PDM data 2 pin number (optional, set to -1 to disable)
     * @param data3_pin: PDM data 3 pin number (optional, set to -1 to disable)
     * @return Returns 0 on success, error code on failure
     */
    static int pdm_audio_pin_config(int clk_pin = 26, int data0_pin = -1, int data1_pin = -1, int data2_pin = -1, int data3_pin = -1);

    /**
     * @brief Open a new audio stream
     *
     * @param rate Sample rate in Hz
     * @param channels Number of audio channels
     * @param format Audio sample format
     * @param input True to create an input stream
     * @param output True to create an output stream
     * @param input_device_index Input device index (-1 for default)
     * @param output_device_index Output device index (-1 for default)
     * @param enable_codec True to enable hardware codec
     * @param frames_per_buffer Number of frames per buffer
     * @param start True to start the stream immediately
     * @return AudioStream* Pointer to the created stream
     */
    AudioStream* open(int rate, int channels, AudioFormat format,
                                bool input = false, bool output = false,
                                int input_device_index = -1, int output_device_index = -1,
                                bool enable_codec = true, int frames_per_buffer = 1024,
                                bool start = true);

    /**
     * @brief Close an audio stream
     *
     * @param stream Pointer to the stream to close
     */
    void close(AudioStream* stream);

    /**
     * @brief Get the sample size in bytes for a given format
     *
     * @param format Audio format
     * @return Sample size in bytes
     */
    int get_sample_size(int format);

    /**
     * @brief Get the AudioFormat from a sample width in bytes
     *
     * @param width Sample width in bytes
     * @return Corresponding AudioFormat
     */
    int get_format_from_width(int width);

private:
    /**
     * @brief Remove a stream from the managed list
     *
     * @param stream Pointer to the stream to remove
     */
    void _remove_stream(AudioStream* stream);

    std::vector<AudioStream*> _streams;  ///< List of managed audio streams
    static bool _vb_init;                ///< Static flag indicating if video buffer is initialized
};


#endif // K230_AUDIO_H