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

#include "mixer/MixerBus.hpp"
#include <cmath>
#include <algorithm>
#include <cstring>

using namespace mixer;

MixerBus::MixerBus(int64_t id, MixerBusType type, const std::string& name, int channels)
    : id_(id)
    , type_(type)
    , name_(name)
    , channelCount_(channels)
{
    buffers_.resize(channels);
}

void MixerBus::setVolume(float db)
{
    volume_ = std::clamp(db, -96.0f, 12.0f);
}

void MixerBus::setMute(bool mute)
{
    mute_ = mute;
}

void MixerBus::allocateBuffers(size_t maxFrames)
{
    maxFrames_ = maxFrames;
    for (auto& buf : buffers_) {
        buf.resize(maxFrames, 0.0f);
    }
}

void MixerBus::clear()
{
    for (auto& buf : buffers_) {
        std::fill(buf.begin(), buf.end(), 0.0f);
    }
}

void MixerBus::accumulate(
    const float* const* source,
    uint32_t frames,
    float gain,
    int sourceChannels)
{
    uint32_t n = std::min(frames, (uint32_t)maxFrames_);
    int nChannels = std::min(sourceChannels, channelCount_);

    if (std::abs(gain) < 0.0001f) return;

    if (std::abs(gain - 1.0f) < 0.0001f) {
        // Unity gain fast path
        for (int ch = 0; ch < nChannels; ++ch) {
            if (ch < (int)buffers_.size() && ch < sourceChannels && source[ch]) {
                float* dst = buffers_[ch].data();
                const float* src = source[ch];
                for (uint32_t i = 0; i < n; ++i) {
                    dst[i] += src[i];
                }
            }
        }
    } else {
        // Scaled accumulation
        for (int ch = 0; ch < nChannels; ++ch) {
            if (ch < (int)buffers_.size() && ch < sourceChannels && source[ch]) {
                float* dst = buffers_[ch].data();
                const float* src = source[ch];
                for (uint32_t i = 0; i < n; ++i) {
                    dst[i] += src[i] * gain;
                }
            }
        }
    }
}

void MixerBus::accumulateMono(
    const float* source,
    uint32_t frames,
    float gain)
{
    if (!source || buffers_.empty()) return;

    uint32_t n = std::min(frames, (uint32_t)maxFrames_);
    float* dst = buffers_[0].data();

    if (std::abs(gain) < 0.0001f) return;

    if (std::abs(gain - 1.0f) < 0.0001f) {
        for (uint32_t i = 0; i < n; ++i) {
            dst[i] += source[i];
        }
    } else {
        for (uint32_t i = 0; i < n; ++i) {
            dst[i] += source[i] * gain;
        }
    }
}

void MixerBus::process(uint32_t frames)
{
    uint32_t n = std::min(frames, (uint32_t)maxFrames_);
    bool isMuted = mute_.load();
    float volumeGain = isMuted ? 0.0f : std::pow(10.0f, volume_.load() / 20.0f);

    // Peak VU tracking
    float leftPeak = -96.0f;
    float rightPeak = -96.0f;

    if (std::abs(volumeGain) < 0.0001f) {
        // Effectively mute
        for (int ch = 0; ch < channelCount_; ++ch) {
            if (ch < (int)buffers_.size()) {
                std::fill(buffers_[ch].begin(), buffers_[ch].begin() + n, 0.0f);
            }
        }
    } else if (std::abs(volumeGain - 1.0f) < 0.001f) {
        // Unity gain — no scaling needed, just compute VU
        for (uint32_t i = 0; i < n; ++i) {
            if (buffers_.size() > 0) {
                float absVal = std::abs(buffers_[0][i]);
                if (absVal > leftPeak) leftPeak = absVal;
            }
            if (buffers_.size() > 1) {
                float absVal = std::abs(buffers_[1][i]);
                if (absVal > rightPeak) rightPeak = absVal;
            }
        }
    } else {
        // Apply volume gain
        for (int ch = 0; ch < channelCount_; ++ch) {
            if (ch >= (int)buffers_.size()) break;
            float* buf = buffers_[ch].data();
            for (uint32_t i = 0; i < n; ++i) {
                buf[i] *= volumeGain;
            }
        }
        // Compute VU from scaled signal
        for (uint32_t i = 0; i < n; ++i) {
            if (buffers_.size() > 0) {
                float absVal = std::abs(buffers_[0][i]);
                if (absVal > leftPeak) leftPeak = absVal;
            }
            if (buffers_.size() > 1) {
                float absVal = std::abs(buffers_[1][i]);
                if (absVal > rightPeak) rightPeak = absVal;
            }
        }
    }

    // Convert peak to dB with decay
    float leftDb = (leftPeak > 0.00001f) ? 20.0f * std::log10(leftPeak) : -96.0f;
    float rightDb = (rightPeak > 0.00001f) ? 20.0f * std::log10(rightPeak) : -96.0f;

    float oldLeft = vuLeft_.load();
    float oldRight = vuRight_.load();

    if (leftDb > oldLeft) {
        vuLeft_ = leftDb;
    } else {
        vuLeft_ = oldLeft * 0.95f + leftDb * 0.05f;
    }

    if (rightDb > oldRight) {
        vuRight_ = rightDb;
    } else {
        vuRight_ = oldRight * 0.95f + rightDb * 0.05f;
    }
}
