#pragma once

#include "Subsystem.hpp"

#include "MayaFlux/Core/Backends/Audio/AudioBackend.hpp"

namespace MayaFlux::Registry::Service {
struct AudioBackendService;
}

namespace MayaFlux::Core {

/**
 * @class AudioSubsystem
 * @brief Audio processing subsystem managing real-time audio I/O and processing
 *
 * Implements the ISubsystem interface to provide audio-specific processing capabilities
 * within the MayaFlux engine. Manages audio backends, devices, and streams while
 * coordinating with the token-based processing architecture for buffer, node and scheduling operations.
 *
 * Uses AUDIO_BACKEND token for buffer processing and AUDIO_RATE token for node processing,
 * enabling real-time audio processing with proper thread safety and resource isolation.
 */
class MAYAFLUX_API AudioSubsystem : public ISubsystem {
public:
    virtual ~AudioSubsystem() = default;

    /** @brief Initialize audio processing with provided handle */
    void initialize(SubsystemProcessingHandle& handle) override;

    /** @brief Register audio backend callbacks for real-time processing */
    void register_callbacks() override;

    /** @brief Start audio processing and streaming */
    void start() override;

    /** @brief Pause audio processing without stopping the stream */
    void pause() override;

    /** @brief Resume audio processing after pause */
    void resume() override;

    /** @brief Stop audio processing and streaming */
    void stop() override;

    /** @brief Shutdown and cleanup audio resources */
    void shutdown() override;

    /** @brief Get audio subsystem token configuration */
    inline SubsystemTokens get_tokens() const override { return m_subsystem_tokens; }

    /** @brief Check if audio subsystem is ready for operation */
    inline bool is_ready() const override { return m_is_ready; }

    /** @brief Check if audio subsystem is currently running */
    inline bool is_running() const override { return m_is_running.load(); }

    /* @brief Wait until the audio subsystem's processing loop is confirmed live */
    void wait_until_running() override;

    /** @brief Get access to the underlying audio backend */
    inline IAudioBackend* get_audio_backend() { return m_audiobackend.get(); }

    /** @brief Get read-only access to stream manager */
    inline const AudioStream* get_stream_manager() const { return m_audio_stream.get(); }

    /** @brief Get read-only access to device manager */
    inline const AudioDevice* get_device_manager() const { return m_audio_device.get(); }

    /** @brief Get global stream configuration */
    inline const GlobalStreamInfo& get_stream_info() const { return m_stream_info; }

    /**
     * @brief Processes input data from audio interface
     * @param input_buffer Pointer to interleaved input data
     * @param num_frames Number of frames to process
     * @return Status code (0 for success)
     *
     * Handles incoming audio data from the audio interface, converting from
     * interleaved format and routing to appropriate buffer channels for processing.
     */
    int process_input(double* input_buffer, unsigned int num_frames);

    /**
     * @brief Processes output data for audio interface
     * @param output_buffer Pointer to interleaved output buffer
     * @param num_frames Number of frames to process
     * @return Status code (0 for success)
     *
     * Processes node graph and buffer operations, then fills the output buffer
     * with processed audio data in interleaved format for the audio interface.
     * This is the main processing entry point called by audio callbacks.
     */
    int process_output(double* output_buffer, unsigned int num_frames);

    /**
     * @brief Processes both input and output data in full-duplex mode
     * @param input_buffer Pointer to input data buffer
     * @param output_buffer Pointer to output data buffer
     * @param num_frames Number of frames to process
     * @return Status code (0 for success)
     *
     * Handles full-duplex audio processing, processing input and generating
     * output simultaneously. Used for real-time effects and monitoring scenarios.
     */
    int process_audio(double* input_buffer, double* output_buffer, unsigned int num_frames);

    /**
     * @brief Constructs AudioSubsystem with stream configuration
     * @param stream_info Global stream configuration
     *
     * Constructor - AudioSubsystem instances are created by Engine.
     * Initializes audio backend and configures processing tokens.
     */
    explicit AudioSubsystem(GlobalStreamInfo& stream_info);

    inline SubsystemType get_type() const override { return m_type; }

    SubsystemProcessingHandle* get_processing_context_handle() override { return m_handle; }

private:
    using ObserverMap = std::unordered_map<uint32_t, std::function<void(const double*, uint32_t)>>;
    void register_backend_service();
    void notify_loop();

    GlobalStreamInfo m_stream_info; ///< Audio stream configuration

    std::unique_ptr<IAudioBackend> m_audiobackend; ///< Audio backend implementation
    std::unique_ptr<AudioDevice> m_audio_device; ///< Audio device manager
    std::unique_ptr<AudioStream> m_audio_stream; ///< Audio stream manager

    std::shared_ptr<Registry::Service::AudioBackendService> m_audio_backend_service;

    SubsystemTokens m_subsystem_tokens; ///< Processing token configuration
    SubsystemProcessingHandle* m_handle; ///< Reference to processing handle

    bool m_is_ready {}; ///< Subsystem ready state
    bool m_is_paused {}; ///< Subsystem paused state

    std::atomic<bool> m_is_running { false }; ///< Subsystem running state
    std::atomic<int> m_callback_active { 0 }; ///< Active callback counter

    std::atomic<uint32_t> m_next_observer_id { 1 };
    std::atomic<bool> m_notify_running { false };

    alignas(64) std::atomic<const double*> m_snapshot_ptr { nullptr };
    std::atomic<uint64_t> m_snapshot_generation { 0 };
    std::atomic<uint32_t> m_snapshot_size { 0 };
    std::vector<double> m_snapshot_copy; ///< Owned copy of the last output cycle; decouples notification-thread reads from the callback write buffer.

    std::thread m_notify_thread;

#ifdef MAYAFLUX_PLATFORM_MACOS
    // Apple Clang lacks std::atomic<std::shared_ptr<T>> - raw pointer + hazard
    std::atomic<const ObserverMap*> m_observers_ptr { nullptr };

    static constexpr size_t MAX_OBSERVER_READERS = 8;
    mutable std::array<std::atomic<const ObserverMap*>, MAX_OBSERVER_READERS> m_obs_hazard_ptrs {};
    mutable std::atomic<size_t> m_obs_hazard_counter { 0 };

    std::pair<const ObserverMap*, size_t> acquire_observers() const;
    void release_observers(size_t slot) const;
    void retire_observers(const ObserverMap* old);
    std::vector<const ObserverMap*> m_obs_retired;
#else
    std::atomic<std::shared_ptr<ObserverMap>> m_observers {
        std::make_shared<ObserverMap>()
    };
#endif

    static const SubsystemType m_type = SubsystemType::AUDIO;
};

}
