#include "PluginProcessor.h"
#include "PluginEditor.h"

SaftpumpeProcessor::SaftpumpeProcessor()
    : AudioProcessor(BusesProperties()
          .withInput("Input", juce::AudioChannelSet::stereo(), true)
          .withOutput("Output", juce::AudioChannelSet::stereo(), true)),
      parameters(*this, nullptr, "Parameters", {
          std::make_unique<juce::AudioParameterFloat>(
              thresholdID, "Threshold", juce::NormalisableRange<float>(-48.0f, 0.0f, 0.1f), -20.0f),
          std::make_unique<juce::AudioParameterFloat>(
              ratioID, "Ratio", juce::NormalisableRange<float>(1.0f, 20.0f, 0.1f, 0.5f), 4.0f),
          std::make_unique<juce::AudioParameterFloat>(
              attackID, "Attack", juce::NormalisableRange<float>(0.001f, 0.1f, 0.001f, 0.5f), 0.03f),
          std::make_unique<juce::AudioParameterFloat>(
              releaseID, "Release", juce::NormalisableRange<float>(0.05f, 1.5f, 0.01f, 0.5f), 0.3f),
          std::make_unique<juce::AudioParameterFloat>(
              kneeID, "Knee", juce::NormalisableRange<float>(0.0f, 12.0f, 0.5f), 6.0f),
          std::make_unique<juce::AudioParameterFloat>(
              makeupID, "Makeup", juce::NormalisableRange<float>(0.0f, 24.0f, 0.1f), 0.0f),
          std::make_unique<juce::AudioParameterFloat>(
              mixID, "Mix", juce::NormalisableRange<float>(0.0f, 1.0f, 0.01f), 1.0f),
           std::make_unique<juce::AudioParameterFloat>(
               autoReleaseID, "Auto Release", juce::NormalisableRange<float>(0.0f, 1.0f, 0.01f), 1.0f)
      })
{
    inputScope.fill(0.0f);
    outputScope.fill(0.0f);
}

SaftpumpeProcessor::~SaftpumpeProcessor() {}

void SaftpumpeProcessor::prepareToPlay(double sampleRate, int samplesPerBlock) {
    compressorL.prepare(sampleRate, samplesPerBlock);
    compressorR.prepare(sampleRate, samplesPerBlock);
    compressorL.reset();
    compressorR.reset();
    scopeIndex = 0;
}

void SaftpumpeProcessor::releaseResources() {}

void SaftpumpeProcessor::processBlock(juce::AudioBuffer<float>& buffer, juce::MidiBuffer&) {
    juce::ScopedNoDenormals noDenormals;

    auto totalNumInputChannels = getTotalNumInputChannels();
    auto totalNumOutputChannels = getTotalNumOutputChannels();

    for (auto i = totalNumInputChannels; i < totalNumOutputChannels; ++i)
        buffer.clear(i, 0, buffer.getNumSamples());

    updateCompressorParams();

    float maxInput = 0.0f;
    float maxOutput = 0.0f;
    float maxGR = 0.0f;

    int numSamples = buffer.getNumSamples();
    float* channelDataL = buffer.getWritePointer(0);
    float* channelDataR = (totalNumInputChannels > 1) ? buffer.getWritePointer(1) : nullptr;

    for (int sample = 0; sample < numSamples; ++sample) {
        float inL = channelDataL[sample];
        float inR = channelDataR ? channelDataR[sample] : inL;

        float absIn = std::max(std::abs(inL), std::abs(inR));
        if (absIn > maxInput) maxInput = absIn;

        inputScope[scopeIndex] = inL;

        float outL = compressorL.processSample(inL);
        float outR = compressorR.processSample(inR);

        channelDataL[sample] = outL;
        if (channelDataR) channelDataR[sample] = outR;

        float absOut = std::max(std::abs(outL), std::abs(outR));
        if (absOut > maxOutput) maxOutput = absOut;

        float gr = std::abs(compressorL.getGainReduction());
        if (gr > maxGR) maxGR = gr;

        outputScope[scopeIndex] = outL;
        scopeIndex = (scopeIndex + 1) % scopeSize;
    }

    currentInputLevel = currentInputLevel * 0.8f + (maxInput > 0.0001f ? 20.0f * std::log10(maxInput) : -100.0f) * 0.2f;
    currentOutputLevel = currentOutputLevel * 0.8f + (maxOutput > 0.0001f ? 20.0f * std::log10(maxOutput) : -100.0f) * 0.2f;
    currentGainReduction = currentGainReduction * 0.7f + maxGR * 0.3f;
}

void SaftpumpeProcessor::updateCompressorParams() {
    CompressorDSP::Params p;
    p.threshold = parameters.getRawParameterValue(thresholdID)->load();
    p.ratio = parameters.getRawParameterValue(ratioID)->load();
    p.attack = parameters.getRawParameterValue(attackID)->load();
    p.release = parameters.getRawParameterValue(releaseID)->load();
    p.knee = parameters.getRawParameterValue(kneeID)->load();
    p.makeup = parameters.getRawParameterValue(makeupID)->load();
    p.mix = parameters.getRawParameterValue(mixID)->load();
    p.autoRelease = parameters.getRawParameterValue(autoReleaseID)->load() > 0.5f;

    compressorL.setParams(p);
    compressorR.setParams(p);
}

juce::AudioProcessorEditor* SaftpumpeProcessor::createEditor() {
    return new SaftpumpeEditor(*this);
}

void SaftpumpeProcessor::getStateInformation(juce::MemoryBlock& destData) {
    auto state = parameters.copyState();
    std::unique_ptr<juce::XmlElement> xml(state.createXml());
    copyXmlToBinary(*xml, destData);
}

void SaftpumpeProcessor::setStateInformation(const void* data, int sizeInBytes) {
    std::unique_ptr<juce::XmlElement> xml(getXmlFromBinary(data, sizeInBytes));
    if (xml && xml->hasTagName(parameters.state.getType())) {
        parameters.replaceState(juce::ValueTree::fromXml(*xml));
    }
}

juce::AudioProcessor* JUCE_CALLTYPE createPluginFilter() {
    return new SaftpumpeProcessor();
}
