/*
  ==============================================================================

    This file contains the basic framework code for a JUCE plugin processor.

  ==============================================================================
*/

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

//==============================================================================
CMLSProjectAudioProcessor::CMLSProjectAudioProcessor()
#ifndef JucePlugin_PreferredChannelConfigurations
     : AudioProcessor (BusesProperties()
                     #if ! JucePlugin_IsMidiEffect
                      #if ! JucePlugin_IsSynth
                       .withInput  ("Input",  juce::AudioChannelSet::stereo(), true)
                      #endif
                       .withOutput ("Output", juce::AudioChannelSet::stereo(), true)
                     #endif
                       ), apvts(*this, nullptr, "Parameters", createParameters())
#endif
{
    // Chaining the effects
	this->equalizer = new CMLSEqualizer();
    this->distortion = new CMLSDistortion();
    this->chorus = new CMLSChorus();
    this->delay = new CMLSDelay();
    this->reverb = new CMLSReverb();

    this->apvtsListeners.setEqualizerProcessor((CMLSEqualizer*)this->equalizer);
    this->apvtsListeners.setDistortionProcssor((CMLSDistortion*)this->distortion);
    this->apvtsListeners.setChorusProcessor((CMLSChorus*)this->chorus);
    this->apvtsListeners.setDelayProcessor((CMLSDelay*)this->delay);
    this->apvtsListeners.setReverbProcessor((CMLSReverb*)this->reverb);

    this->apvts.addParameterListener("EQLOWBANDGAIN", &apvtsListeners);
    this->apvts.addParameterListener("EQHIGHBANDGAIN", &apvtsListeners);
    this->apvts.addParameterListener("DISTORTIONDRIVE", &apvtsListeners);
    this->apvts.addParameterListener("DISTORTIONMIX", &apvtsListeners);
    this->apvts.addParameterListener("CHORUSDRYWET", &apvtsListeners);
    this->apvts.addParameterListener("CHORUSAMOUNT", &apvtsListeners);
    this->apvts.addParameterListener("DELAYDRYWET", &apvtsListeners);
    this->apvts.addParameterListener("DELAYAMOUNT", &apvtsListeners);
    this->apvts.addParameterListener("REVERBROOMSIZE", &apvtsListeners);
    this->apvts.addParameterListener("REVERBDRYWET", &apvtsListeners);

    this->processorChain.pushProcessor(*equalizer);
    this->processorChain.pushProcessor(*distortion);
    this->processorChain.pushProcessor(*chorus);
    this->processorChain.pushProcessor(*delay);
    this->processorChain.pushProcessor(*reverb);

    this->processorChain.reset();
}

CMLSProjectAudioProcessor::~CMLSProjectAudioProcessor()
{
}

//==============================================================================
const juce::String CMLSProjectAudioProcessor::getName() const
{
    return JucePlugin_Name;
}

bool CMLSProjectAudioProcessor::acceptsMidi() const
{
   #if JucePlugin_WantsMidiInput
    return true;
   #else
    return false;
   #endif
}

bool CMLSProjectAudioProcessor::producesMidi() const
{
   #if JucePlugin_ProducesMidiOutput
    return true;
   #else
    return false;
   #endif
}

bool CMLSProjectAudioProcessor::isMidiEffect() const
{
   #if JucePlugin_IsMidiEffect
    return true;
   #else
    return false;
   #endif
}

double CMLSProjectAudioProcessor::getTailLengthSeconds() const
{
    return 0.0;
}

int CMLSProjectAudioProcessor::getNumPrograms()
{
    return 1;   // NB: some hosts don't cope very well if you tell them there are 0 programs,
                // so this should be at least 1, even if you're not really implementing programs.
}

int CMLSProjectAudioProcessor::getCurrentProgram()
{
    return 0;
}

void CMLSProjectAudioProcessor::setCurrentProgram (int index)
{
}

const juce::String CMLSProjectAudioProcessor::getProgramName (int index)
{
    return {};
}

void CMLSProjectAudioProcessor::changeProgramName (int index, const juce::String& newName)
{
}

//==============================================================================
void CMLSProjectAudioProcessor::prepareToPlay (double sampleRate, int samplesPerBlock)
{
    // Use this method as the place to do any pre-playback
    // initialisation that you need..
    juce::dsp::ProcessSpec spec;
    spec.maximumBlockSize = samplesPerBlock;
    spec.numChannels = getTotalNumOutputChannels();
    spec.sampleRate = sampleRate;

	this->processorChain.prepare(spec);
}

void CMLSProjectAudioProcessor::releaseResources()
{
    // When playback stops, you can use this as an opportunity to free up any
    // spare memory, etc.
}

#ifndef JucePlugin_PreferredChannelConfigurations
bool CMLSProjectAudioProcessor::isBusesLayoutSupported (const BusesLayout& layouts) const
{
  #if JucePlugin_IsMidiEffect
    juce::ignoreUnused (layouts);
    return true;
  #else
    // This is the place where you check if the layout is supported.
    // In this template code we only support mono or stereo.
    // Some plugin hosts, such as certain GarageBand versions, will only
    // load plugins that support stereo bus layouts.
    if (layouts.getMainOutputChannelSet() != juce::AudioChannelSet::mono()
     && layouts.getMainOutputChannelSet() != juce::AudioChannelSet::stereo())
        return false;

    // This checks if the input layout matches the output layout
   #if ! JucePlugin_IsSynth
    if (layouts.getMainOutputChannelSet() != layouts.getMainInputChannelSet())
        return false;
   #endif

    return true;
  #endif
}
#endif

void CMLSProjectAudioProcessor::processBlock (juce::AudioBuffer<float>& buffer, juce::MidiBuffer& midiMessages)
{
    juce::ScopedNoDenormals noDenormals;
    auto totalNumInputChannels  = getTotalNumInputChannels();
    auto totalNumOutputChannels = getTotalNumOutputChannels();

    const auto numChannels = juce::jmax(totalNumInputChannels, totalNumOutputChannels);

    auto audioBlock = juce::dsp::AudioBlock<float>(buffer).getSubsetChannelBlock(0, (int)numChannels);
    auto context = juce::dsp::ProcessContextReplacing<float>(audioBlock);

    /* Processing */
	this->processorChain.process(context);
}

//==============================================================================
bool CMLSProjectAudioProcessor::hasEditor() const
{
    return true; // (change this to false if you choose to not supply an editor)
}

juce::AudioProcessorEditor* CMLSProjectAudioProcessor::createEditor()
{
    return new juce::GenericAudioProcessorEditor (*this);
}

//==============================================================================
void CMLSProjectAudioProcessor::getStateInformation (juce::MemoryBlock& destData)
{
    // You should use this method to store your parameters in the memory block.
    // You could do that either as raw data, or use the XML or ValueTree classes
    // as intermediaries to make it easy to save and load complex data.
}

void CMLSProjectAudioProcessor::setStateInformation (const void* data, int sizeInBytes)
{
    // You should use this method to restore your parameters from this memory block,
    // whose contents will have been created by the getStateInformation() call.
}

void CMLSProjectAudioProcessor::muteEffectInSlot(int slot) {
    this->processorChain.muteProcessrInSlot(slot);
}

void CMLSProjectAudioProcessor::unmuteEffectInSlot(int slot) {
    this->processorChain.unmuteProcessorInSlot(slot);
}

void CMLSProjectAudioProcessor::swapEffectInSlot(int slot1, int slot2) {
	this->processorChain.swapPlaces(slot1, slot2);
}

juce::AudioProcessorValueTreeState* CMLSProjectAudioProcessor::getApvts() {
    return &this->apvts;
}

//==============================================================================
// This creates new instances of the plugin..
juce::AudioProcessor* JUCE_CALLTYPE createPluginFilter()
{
    return new CMLSProjectAudioProcessor();
}

juce::AudioProcessorValueTreeState::ParameterLayout CMLSProjectAudioProcessor::createParameters() {
    std::vector<std::unique_ptr<juce::RangedAudioParameter>> params;
    
    // Equalizer parameters
    params.push_back(std::make_unique<juce::AudioParameterFloat>(
        "EQLOWBANDGAIN",
        "EqLowBandGain",
        -60.0f,
        20.0f,
        0.0f
    ));
    params.push_back(std::make_unique<juce::AudioParameterFloat>(
        "EQHIGHBANDGAIN",
        "EqHighBandGain",
        -60.0f,
        20.0f,
        0.0f
    ));

    // Distortion parameter
    params.push_back(std::make_unique<juce::AudioParameterFloat>(
        "DISTORTIONDRIVE",
        "DistortionDrive",
        1.0f,
        15.0f,
        1.0f
    ));
    params.push_back(std::make_unique<juce::AudioParameterFloat>(
        "DISTORTIONMIX",
        "DistortionMix",
        0.0f,
        1.0f,
        0.0f
    ));

    // Chorus parameter
    params.push_back(std::make_unique<juce::AudioParameterFloat>(
        "CHORUSDRYWET",
        "ChorusDryWet",
        0.0f,
        1.0f,
        0.0f
    ));
    params.push_back(std::make_unique<juce::AudioParameterFloat>(
        "CHORUSAMOUNT",
        "ChorusAmount",
        0.0f,
        1.0f,
        0.0f
    ));

    // Delay parameter
    params.push_back(std::make_unique<juce::AudioParameterFloat>(
        "DELAYDRYWET",
        "DelayDryWet",
        0.0f,
        1.0f,
        0.0f
    ));
    params.push_back(std::make_unique<juce::AudioParameterFloat>(
        "DELAYAMOUNT",
        "DelayAmount",
        0.0f,
        1.0f,
        0.0f
    ));

    // Reverb parameters
    params.push_back(std::make_unique<juce::AudioParameterFloat>(
        "REVERBROOMSIZE",
        "ReverbRoomSize",
        0.0f,
        1.0f,
        0.0f
    ));
    params.push_back(std::make_unique<juce::AudioParameterFloat>(
        "REVERBDRYWET",
        "ReverbDryWet",
        0.0f,
        1.0f,
        0.0f
    ));

    return { params.begin(), params.end() };
}