// Copyright (c) 2026 Ourpad Network
// See LICENSE file in the project root for full license text.

#pragma once

#include <memory>
#include <vector>
#include <map>
#include <atomic>
#include <cstdint>
#include <functional>
#include "mixer/MixerChannelStrip.hpp"
#include "mixer/MixerBus.hpp"
#include "mixer/Types.hpp"

namespace mixer {

class MixerChannelStrip;
class MixerBus;

/// The MixerEngine is the heart of the band-in-a-box digital mixer.
///
/// It owns and manages:
///   - N channel strips (MixerChannelStrip), one per physical/logical input
///   - M buses (MixerBus), including master, subgroups, aux sends
///   - A routing graph connecting channels to buses and buses to buses
///
/// Processing order per audio cycle:
///   1. Clear all bus buffers
///   2. For each channel: apply volume/pan → accumulate to routed buses
///   3. For each aux send: calculate send level, accumulate to aux buses
///   4. Route buses to buses according to routing matrix
///   5. Process each bus (apply volume, compute VU)
///   6. Master bus outputs are the final mix
///
/// All control methods are thread-safe for use from the non-RT thread.
/// The process() method runs in the RT audio thread.
class MixerEngine {
public:
    MixerEngine();
    ~MixerEngine();

    // Disable copy
    MixerEngine(const MixerEngine&) = delete;
    MixerEngine& operator=(const MixerEngine&) = delete;

    /// --- Configuration ---

    /// Set sample rate and max buffer size before preparation
    void setSampleRate(uint32_t sampleRate);
    void setMaxBufferSize(size_t frames);

    /// --- Channel Management ---

    /// Add a new channel strip for the given physical input index.
    /// Returns a pointer to the new channel (valid until removed).
    MixerChannelStrip* addChannel(int physicalInputIndex);

    /// Auto-create channels based on the number of detected input channels.
    /// Removes existing channels and creates one strip per input.
    /// Also sets up default output routes.
    void autoCreateChannels(uint32_t inputChannelCount, uint32_t outputChannelCount = 2);

    /// Remove a channel by its channel index.
    void removeChannel(int channelIndex);

    /// Get a channel by index. Returns nullptr if not found.
    MixerChannelStrip* getChannel(int channelIndex);
    const MixerChannelStrip* getChannel(int channelIndex) const;

    /// Number of channels currently in the mixer.
    size_t channelCount() const { return channels_.size(); }

    /// --- Bus Management ---

    /// Add a new bus and return its ID.
    int64_t addBus(MixerBusType type, const std::string& name, int channels = 2);

    /// Remove a bus by ID.
    void removeBus(int64_t busId);

    /// Get a bus by ID. Returns nullptr if not found.
    MixerBus* getBus(int64_t busId);
    const MixerBus* getBus(int64_t busId) const;

    /// Access the master bus (always present).
    MixerBus* masterBus() { return masterBus_; }
    const MixerBus* masterBus() const { return masterBus_; }

    /// Get all bus IDs (for iteration).
    std::vector<int64_t> busIds() const;

    /// --- Routing ---

    /// Route a channel to a bus with a given level in dB.
    void routeChannelToBus(int channelIndex, int64_t busId, float levelDb = 0.0f);

    /// Route a bus to another bus (e.g., subgroup to master).
    void routeBusToBus(int64_t sourceBusId, int64_t targetBusId, float levelDb = 0.0f);

    /// Remove a route.
    void removeRoute(int64_t sourceId, int64_t targetBusId);

    /// Clear all routes.
    void clearRoutes();

    /// --- Output Routing ---

    /// Get the current output routing table (bus → physical output channel mapping).
    const std::vector<MixerOutputRoute>& outputRoutes() const { return outputRoutes_; }

    /// Set the entire output routing table.
    void setOutputRoutes(const std::vector<MixerOutputRoute>& routes);

    /// Add a single output route.
    void addOutputRoute(int64_t busId, int sourceStartChannel, int targetStartChannel, int channels);

    /// Remove all output routes for a given bus.
    void removeOutputRoutes(int64_t busId);

    /// Get all routes for a given bus.
    std::vector<MixerOutputRoute> findOutputRoutesForBus(int64_t busId) const;

    /// Get all current routes.
    const std::vector<MixerRouteEntry>& routes() const { return routes_; }

    /// --- Physical I/O Info ---

    /// Number of physical input channels detected on the audio device.
    uint32_t physicalInputCount() const { return physicalInputCount_; }
    /// Number of physical output channels detected on the audio device.
    uint32_t physicalOutputCount() const { return physicalOutputCount_; }

    /// Set the physical I/O channel counts.
    void setPhysicalChannelCounts(uint32_t inputs, uint32_t outputs) {
        physicalInputCount_ = inputs;
        physicalOutputCount_ = outputs;
    }

    /// --- Audio Processing (real-time thread) ---

    /// Prepare all channels and allocate buffers.
    void Prepare();

    /// Activate all channels.
    void Activate();

    /// Deactivate all channels.
    void Deactivate();

    /// Process one full mixer cycle.
    /// deviceInputs/outputs are the raw audio interface buffers.
    void process(
        float** deviceInputs,
        uint32_t inputChannels,
        float** deviceOutputs,
        uint32_t outputChannels,
        uint32_t frames
    );

    /// --- Solo Management ---

    /// True if any channel has solo engaged.
    bool anySoloActive() const;

    /// --- State Serialization ---

    MixerSnapshot captureSnapshot() const;
    void applySnapshot(const MixerSnapshot& snapshot);

private:
    std::vector<std::unique_ptr<MixerChannelStrip>> channels_;
    std::map<int64_t, std::unique_ptr<MixerBus>> buses_;
    MixerBus* masterBus_ = nullptr;

    // Routing entries
    std::vector<MixerRouteEntry> routes_;

    // Output routing entries (bus → physical output channel mapping)
    std::vector<MixerOutputRoute> outputRoutes_;

    // Audio configuration
    uint32_t sampleRate_ = 48000;
    size_t maxBufferSize_ = 512;

    // Next bus ID counter
    static std::atomic<int64_t> nextBusId_;

    // Temporary per-channel output buffers for routing
    std::vector<std::vector<float>> channelOutputBuffers_;

    // Physical I/O channel counts
    uint32_t physicalInputCount_ = 0;
    uint32_t physicalOutputCount_ = 0;

    // Internal helper: accumulate a channel's output to all its routed buses
    void routeChannelOutput(
        MixerChannelStrip* channel,
        float** channelOutput,
        uint32_t frames
    );

    // Internal helper: process all bus-to-bus routing
    void processBusRouting(uint32_t frames);

    // Build a list of all routes from a given source
    std::vector<MixerRouteEntry*> findRoutesForSource(int64_t sourceId);
};

} // namespace mixer
