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

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

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

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

//==============================================================================
ReverbAudioProcessor::ReverbAudioProcessor()
#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
                       )
#endif
{

	addParameter(roomSize = new juce::AudioParameterFloat(juce::ParameterID("roomSize", 1), "Room Size", 0.0, 1.0, 0.5));
	addParameter(damping = new juce::AudioParameterFloat(juce::ParameterID("damping", 2), "Damping", 0.0, 1.0, 0.5));
	addParameter(wet_dry = new juce::AudioParameterFloat(juce::ParameterID("wet_dry", 3), "Mix", 0.0, 1.0, 0.5));
	addParameter(width = new juce::AudioParameterFloat(juce::ParameterID("width", 4), "Width", 0.0, 1.0, 0.5));
	addParameter(freezeMode = new juce::AudioParameterFloat(juce::ParameterID("freezeMode", 5), "Freeze", 0.0, 1.0, 0.0));
	addParameter(lofi = new juce::AudioParameterBool(juce::ParameterID("lofi", 6), "Tone or lofi", false));
	addParameter(tone_val = new juce::AudioParameterFloat(juce::ParameterID("tone_val", 7), "Tone", 0.0, 1.0, 0.5));
	addParameter(out = new juce::AudioParameterFloat(juce::ParameterID("out", 8), "Output", 0.0, 2.0, 1.0));

	verb.setSampleRate(sample_rate);
}

ReverbAudioProcessor::~ReverbAudioProcessor()
{
}

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

bool ReverbAudioProcessor::acceptsMidi() const
{
	return false;
}

bool ReverbAudioProcessor::producesMidi() const
{
	return false;
}

bool ReverbAudioProcessor::isMidiEffect() const
{
	return false;
}

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

int ReverbAudioProcessor::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 ReverbAudioProcessor::getCurrentProgram()
{
    return 0;
}

void ReverbAudioProcessor::setCurrentProgram (int index)
{
}

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

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

//==============================================================================
void ReverbAudioProcessor::prepareToPlay (double sampleRate, int samplesPerBlock)
{
    // Use this method as the place to do any pre-playback
    // initialisation that you need..
    verb.reset();
	
    juce::IIRFilter* one_l = new juce::IIRFilter();
    juce::IIRFilter* one_r = new juce::IIRFilter();
    juce::IIRFilter* two_l = new juce::IIRFilter();
    juce::IIRFilter* two_r = new juce::IIRFilter();
    juce::IIRFilter* three_l = new juce::IIRFilter();
    juce::IIRFilter* three_r = new juce::IIRFilter();

    filters_l.push_back(one_l);
    filters_l.push_back(two_l);
    filters_l.push_back(three_l);

    filters_r.push_back(one_r);
    filters_r.push_back(two_r);
    filters_r.push_back(three_r);

}

void ReverbAudioProcessor::releaseResources()
{
    // When playback stops, you can use this as an opportunity to free up any
    // spare memory, etc.
}
#ifndef JucePlugin_PreferredChannelConfigurations
bool ReverbAudioProcessor::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 ReverbAudioProcessor::processBlock (juce::AudioBuffer<float>& buffer, juce::MidiBuffer& midiMessages)
{
    juce::ScopedNoDenormals noDenormals;
    auto totalNumInputChannels  = getTotalNumInputChannels();
    auto totalNumOutputChannels = getTotalNumOutputChannels();
    if (totalNumInputChannels != 2) // Logic will fail AU tests if this isn't here
	    return; 


    // In case we have more outputs than inputs, this code clears any output
    // channels that didn't contain input data, (because these aren't
    // guaranteed to be empty - they may contain garbage).
    // This is here to avoid people getting screaming feedback
    // when they first compile a plugin, but obviously you don't need to keep
    // this code if your algorithm always overwrites all the output channels.
    for (auto i = totalNumInputChannels; i < totalNumOutputChannels; ++i)
        buffer.clear (i, 0, buffer.getNumSamples());

    // This is the place where you'd normally do the guts of your plugin's
    // audio processing...
    // Make sure to reset the state if your inner loop is processing
    // the samples and the outer loop is handling the channels.
    // Alternatively, you can process the samples with the channels
    // interleaved by keeping the same state.

   

    auto* channel_one = buffer.getWritePointer(0);
    auto* channel_two = buffer.getWritePointer(1);
    auto num_samples = buffer.getNumSamples();

    update_verb();
    verb.processStereo(channel_one, channel_two, 
		    num_samples);

    setup_filter();
    for (int i = 0; i < 3; i++) {
	filters_l[i]->processSamples(channel_one, num_samples);
	filters_r[i]->processSamples(channel_two, num_samples);
    }

    //filter_l->processSamples(channel_one, num_samples);
    //filter_r->processSamples(channel_two, num_samples);

	
    for (auto i = 0; i < num_samples; i++) {
	*buffer.getWritePointer(0, i) = (*buffer.getReadPointer(0, i)) * out->get();
	*buffer.getWritePointer(1, i) = (*buffer.getReadPointer(1, i)) * out->get();
    }

}

void ReverbAudioProcessor::setup_filter()
{
	juce::IIRCoefficients coef = juce::IIRCoefficients();
	if (lofi->get()) {
		for (int i = 0; i < 3; i++) {
			//filters_l[i]->reset();
			//filters_r[i]->reset();
		}

		filters_l[0]->setCoefficients(coef.makeBandPass(sample_rate, tone_val->get() * 24000));
		filters_r[0]->setCoefficients(coef.makeBandPass(sample_rate, tone_val->get() * 24000));

		for (int i = 1; i < 3; i++) {
			filters_r[i]->makeInactive();
			filters_l[i]->makeInactive();
		}
	} else {
		for (int i = 0; i < 3; i++) {
			//filters_l[i]->reset();
			//filters_r[i]->reset();
		}

		filters_l[0]->setCoefficients(coef.makeHighPass(sample_rate, 85.0));
		filters_r[0]->setCoefficients(coef.makeHighPass(sample_rate, 85.0));

		float expanded_freq = 0;
		if (tone_val->get() == 0.5) {
			expanded_freq = 12000.0;
		} else if (tone_val->get() < 0.5) {
			expanded_freq = (tone_val->get() * 2) * (12000 - 5000) + 5000; // need to expand the normalized range
		} else {
			expanded_freq = ((tone_val->get() - 0.5) * 2) * (20000 - 12000) + 12000; // need to expand the normalized range
		}

		filters_l[1]->setCoefficients(coef.makeLowPass(sample_rate, expanded_freq));
		filters_r[1]->setCoefficients(coef.makeLowPass(sample_rate, expanded_freq));

		//filters_l[2]->setCoefficients(coef.makePeakFilter(sample_rate, 6000, 0.4, 1));
		//filters_r[2]->setCoefficients(coef.makePeakFilter(sample_rate, 6000, 0.4, 1));

		filters_l[2]->makeInactive(); // band boost for later?
		filters_r[2]->makeInactive();

	}
}

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

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

void ReverbAudioProcessor::update_verb()
{
	params.roomSize = roomSize->get();
	params.damping = damping->get();
	params.wetLevel = wet_dry->get();
	params.dryLevel = 1.0 - wet_dry->get();
	params.width = width->get();
	params.freezeMode = freezeMode->get();

	verb.setParameters(params);
}

//==============================================================================
void ReverbAudioProcessor::getStateInformation (juce::MemoryBlock& destData)
{
	juce::MemoryOutputStream stream(destData, true);

	stream.writeFloat(*roomSize);
	stream.writeFloat(*damping);
	stream.writeFloat(*wet_dry);
	stream.writeFloat(*width);
	stream.writeFloat(*freezeMode);
	stream.writeBool(*lofi);
	stream.writeFloat(*tone_val);
	stream.writeFloat(*out);
}

void ReverbAudioProcessor::setStateInformation (const void* data, int sizeInBytes)
{
	juce::MemoryInputStream stream(data, static_cast<size_t> (sizeInBytes), false);
	roomSize->setValueNotifyingHost(stream.readFloat());
	damping->setValueNotifyingHost(stream.readFloat());
	wet_dry->setValueNotifyingHost(stream.readFloat());
	width->setValueNotifyingHost(stream.readFloat());
	freezeMode->setValueNotifyingHost(stream.readFloat());
	lofi->setValueNotifyingHost(stream.readBool());
	tone_val->setValueNotifyingHost(stream.readFloat());
	out->setValueNotifyingHost(stream.readFloat());
}

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