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

    PluginProcessor.cpp
    Created: 4 Nov 2025 6:20:37pm
    Author:  mickl

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

#include "PluginProcessor.h"



//==============================================================================
WebViewPluginAudioProcessor::WebViewPluginAudioProcessor(AudioProcessorValueTreeState::ParameterLayout layout)
	: AudioProcessor(BusesProperties()
		.withInput("Input", juce::AudioChannelSet::stereo(), true)
		.withOutput("Output", juce::AudioChannelSet::stereo(), true)
	),
	parameters(layout),
	state(*this, nullptr, "STATE", std::move(layout))
{
	shifter.Init(48000.0f, 48);
	shifter.SetFormantPreserve(state.getParameterAsValue("formantPreserve").getValue());
	shifter.SetAutoTuneSpeed(state.getParameterAsValue("autoTuneSpeed").getValue());
	shifter.SetAutoTuneDepth(state.getParameterAsValue("autoTuneDepth").getValue());
	shifter.SetPortamentoTime(state.getParameterAsValue("portTime").getValue());
}

//==============================================================================
void WebViewPluginAudioProcessor::prepareToPlay(double sampleRate, int samplesPerBlock)
{
	const auto channels = std::max(getTotalNumInputChannels(), getTotalNumOutputChannels());
	shifter.Init((float)sampleRate, samplesPerBlock);
	if (channels == 0)
		return;

	filter.prepare({ sampleRate, (uint32_t)samplesPerBlock, (uint32_t)channels });
	filter.reset();
}

bool WebViewPluginAudioProcessor::isBusesLayoutSupported(const BusesLayout& layouts) const
{
	if (layouts.getMainOutputChannelSet() != juce::AudioChannelSet::mono()
		&& layouts.getMainOutputChannelSet() != juce::AudioChannelSet::stereo())
		return false;

	if (layouts.getMainOutputChannelSet() != layouts.getMainInputChannelSet())
		return false;

	return true;
}

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

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

	for (auto i = totalNumInputChannels; i < totalNumOutputChannels; ++i)
		buffer.clear(i, 0, buffer.getNumSamples());
	shifter.SetFormantPreserve(state.getParameterAsValue("formantPreserve").getValue());
	shifter.SetAutoTuneSpeed(state.getParameterAsValue("autoTuneSpeed").getValue());
	shifter.SetAutoTuneDepth(state.getParameterAsValue("autoTuneDepth").getValue());
	shifter.SetPortamentoTime(state.getParameterAsValue("portTime").getValue());
	shifter.SetHarmonyMix(state.getParameterAsValue("harmonyMix").getValue());
	shifter.SetAutoTuneEnable(state.getParameterAsValue("autoTuneEnabled").getValue());
	shifter.SetFreeze(state.getParameterAsValue("freezeEnabled").getValue());
	shifter.SetFreezePitchAdjust(state.getParameterAsValue("freezePitch").getValue());
	shifter.SetFreezeVolume(state.getParameterAsValue("freezeVolume").getValue());
	shifter.SetPanWidth(state.getParameterAsValue("panWidth").getValue());


	juce::AudioBuffer<float> const_buff;
	const_buff.makeCopyOf(buffer);
	shifter.Process(const_buff.getArrayOfReadPointers(), (float**)buffer.getArrayOfWritePointers(), buffer.getNumSamples());

	for (const auto metadata : midi)
	{
		const auto msg = metadata.getMessage();
		if (msg.isNoteOn()) {
			shifter.AddMidiNote(msg.getNoteNumber());
			new_midi = true;
		}
		else if (msg.isNoteOff()) {
			shifter.RemoveMidiNote(msg.getNoteNumber());
			new_midi = true;
		}
	}
}

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

void WebViewPluginAudioProcessor::setStateInformation(const void* data, int sizeInBytes)
{
	std::unique_ptr<juce::XmlElement> xmlState(getXmlFromBinary(data, sizeInBytes));

	if (xmlState.get() != nullptr && xmlState->hasTagName(state.state.getType()))
		state.replaceState(juce::ValueTree::fromXml(*xmlState));
}
