//-----------------------------------------------------------------------------
// Project     : VST SDK
//
// Category    : Helpers
// Filename    : public.sdk/source/vst/vstaudioprocessoralgo.h
// Created by  : Steinberg, 04/2015
// Description : Helper algo for AudioBusBuffers
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------

#pragma once

#include "pluginterfaces/vst/ivstaudioprocessor.h"
#include "pluginterfaces/vst/ivstevents.h"
#include "pluginterfaces/vst/ivstparameterchanges.h"

#include <algorithm>
#include <cmath>

namespace Steinberg {
namespace Vst {

//---helpers---------
/** Returns the current channelBuffers used (depending of symbolicSampleSize). */
inline void** getChannelBuffersPointer (const ProcessSetup& processSetup,
                                 const AudioBusBuffers& bufs)
{
	if (processSetup.symbolicSampleSize == kSample32)
		return (void**)bufs.channelBuffers32;
	return (void**)bufs.channelBuffers64;
}

/** Returns the size in bytes of numSamples for one channel depending of symbolicSampleSize.*/
inline uint32 getSampleFramesSizeInBytes (const ProcessSetup& processSetup, int32 numSamples)
{
	if (processSetup.symbolicSampleSize == kSample32)
		return numSamples * sizeof (Sample32);
	return numSamples * sizeof (Sample64);
}

/** return the bit-mask of channels for the given number of channel
* for example:
* numChannels = 1 => 0b0001 (binar) = 0x01 = 1 (decimal)
* numChannels = 2 => 0b0011 (binar) = 0x03 = 3 (decimal)
* numChannels = 6 => 0b0011 1111 (binar) = 0x3F = 63 (decimal)
*/
inline uint64 getChannelMask (int32 numChannels)
{
	if (numChannels >= 64)
		return kMaxInt64u;

	return ((uint64)1 << numChannels) - 1;
}

namespace Algo {

//------------------------------------------------------------------------
template <typename T>
inline void foreach (AudioBusBuffers* audioBusBuffers, int32 busCount, const T& func)
{
	if (!audioBusBuffers)
		return;

	for (int32 busIndex = 0; busIndex < busCount; ++busIndex)
	{
		func (audioBusBuffers[busIndex]);
	}
}

//------------------------------------------------------------------------
template <typename T>
inline void foreach32 (AudioBusBuffers& audioBuffer, const T& func)
{
	for (int32 channelIndex = 0; channelIndex < audioBuffer.numChannels; ++channelIndex)
	{
		if (!audioBuffer.channelBuffers32[channelIndex])
			return;

		func (audioBuffer.channelBuffers32[channelIndex]);
	}
}

//------------------------------------------------------------------------
template <typename T>
inline void foreach64 (AudioBusBuffers& audioBuffer, const T& func)
{
	for (int32 channelIndex = 0; channelIndex < audioBuffer.numChannels; ++channelIndex)
	{
		if (!audioBuffer.channelBuffers64[channelIndex])
			return;

		func (audioBuffer.channelBuffers64[channelIndex]);
	}
}

//------------------------------------------------------------------------
template <typename T>
inline void foreach32 (AudioBusBuffers& buffer1, AudioBusBuffers& buffer2, const T& func)
{
	int32 numChannels = std::min<int32> (buffer1.numChannels, buffer2.numChannels);

	for (int32 channelIndex = 0; channelIndex < numChannels; ++channelIndex)
	{
		func (buffer1.channelBuffers32[channelIndex], buffer2.channelBuffers32[channelIndex],
		      channelIndex);
	}
}

//------------------------------------------------------------------------
template <typename T>
inline void foreach64 (AudioBusBuffers& buffer1, AudioBusBuffers& buffer2, const T& func)
{
	int32 numChannels = std::min<int32> (buffer1.numChannels, buffer2.numChannels);

	for (int32 channelIndex = 0; channelIndex < numChannels; ++channelIndex)
	{
		func (buffer1.channelBuffers64[channelIndex], buffer2.channelBuffers64[channelIndex],
		      channelIndex);
	}
}

//------------------------------------------------------------------------
inline void copy32 (AudioBusBuffers* src, AudioBusBuffers* dest, int32 sliceSize, int32 startIndex)
{
	if (!src || !dest)
		return;

	int32 numChannels = std::min<int32> (src->numChannels, dest->numChannels);
	size_t numBytes = sliceSize * sizeof (Sample32);
	for (int32 chIdx = 0; chIdx < numChannels; ++chIdx)
	{
		memcpy (&dest->channelBuffers32[chIdx][startIndex], src->channelBuffers32[chIdx], numBytes);
	}
}

//------------------------------------------------------------------------
inline void copy64 (AudioBusBuffers* src, AudioBusBuffers* dest, int32 sliceSize, int32 startIndex)
{
	if (!src || !dest)
		return;

	int32 numChannels = std::min<int32> (src->numChannels, dest->numChannels);
	size_t numBytes = sliceSize * sizeof (Sample64);
	for (int32 chIdx = 0; chIdx < numChannels; ++chIdx)
	{
		memcpy (&dest->channelBuffers64[chIdx][startIndex], src->channelBuffers64[chIdx], numBytes);
	}
}

//------------------------------------------------------------------------
inline void clear32 (AudioBusBuffers* audioBusBuffers, int32 sampleCount, int32 busCount = 1)
{
	if (!audioBusBuffers)
		return;

	const int32 numBytes = sampleCount * sizeof (Sample32);
	foreach (audioBusBuffers, busCount, [&] (AudioBusBuffers& audioBuffer) {
		foreach32 (audioBuffer,
		           [&] (Sample32* channelBuffer) { memset (channelBuffer, 0, numBytes); });
	});
}

//------------------------------------------------------------------------
inline void clear64 (AudioBusBuffers* audioBusBuffers, int32 sampleCount, int32 busCount = 1)
{
	if (!audioBusBuffers)
		return;

	const int32 numBytes = sampleCount * sizeof (Sample64);
	foreach (audioBusBuffers, busCount, [&] (AudioBusBuffers& audioBuffer) {
		foreach64 (audioBuffer,
		           [&] (Sample64* channelBuffer) { memset (channelBuffer, 0, numBytes); });
	});
}

//------------------------------------------------------------------------
inline void mix32 (AudioBusBuffers& src, AudioBusBuffers& dest, int32 sampleCount)
{
	foreach32 (src, dest, [&] (Sample32* srcBuffer, Sample32* destBuffer, int32 /*channelIndex*/) {
		for (int32 sampleIndex = 0; sampleIndex < sampleCount; ++sampleIndex)
			destBuffer[sampleIndex] += srcBuffer[sampleIndex];
	});
}

//------------------------------------------------------------------------
inline void mix64 (AudioBusBuffers& src, AudioBusBuffers& dest, int32 sampleCount)
{
	foreach64 (src, dest, [&] (Sample64* srcBuffer, Sample64* destBuffer, int32 /*channelIndex*/) {
		for (int32 sampleIndex = 0; sampleIndex < sampleCount; ++sampleIndex)
			destBuffer[sampleIndex] += srcBuffer[sampleIndex];
	});
}

//------------------------------------------------------------------------
/* Multiply buffer with a constant */
template <typename T>
inline void multiply(T* srcBuffer, T* destBuffer, int32 sampleCount, T factor)
{
	for (int32 sampleIndex = 0; sampleIndex < sampleCount; ++sampleIndex)
		destBuffer[sampleIndex] = srcBuffer[sampleIndex] * factor;
}

//------------------------------------------------------------------------
/* Multiply all channels of AudioBusBuffer with a constant */
inline void multiply32 (AudioBusBuffers& src, AudioBusBuffers& dest, int32 sampleCount, float factor)
{
	foreach32 (src, dest, [&](Sample32* srcBuffer, Sample32* destBuffer, int32 /*channelIndex*/) {
		multiply (srcBuffer, destBuffer, sampleCount, factor);
	});
}

//------------------------------------------------------------------------
/* Multiply all channels of AudioBusBuffer with a constant */
inline void multiply64 (AudioBusBuffers& src, AudioBusBuffers& dest, int32 sampleCount, double factor)
{
	foreach64 (src, dest, [&] (Sample64* srcBuffer, Sample64* destBuffer, int32 /*channelIndex*/) {
		multiply (srcBuffer, destBuffer, sampleCount, factor);
	});
}

//------------------------------------------------------------------------
inline bool isSilent32 (AudioBusBuffers& audioBuffer, int32 sampleCount, int32 startIndex = 0)
{
	const float epsilon = 1e-10f; // under -200dB...

	sampleCount += startIndex;
	for (int32 channelIndex = 0; channelIndex < audioBuffer.numChannels; ++channelIndex)
	{
		if (!audioBuffer.channelBuffers32[channelIndex])
			return true;

		for (int32 sampleIndex = startIndex; sampleIndex < sampleCount; ++sampleIndex)
		{
			float val = audioBuffer.channelBuffers32[channelIndex][sampleIndex];
			if (std::abs (val) > epsilon)
				return false;
		}
	}

	return true;
}

//------------------------------------------------------------------------
inline bool isSilent64 (AudioBusBuffers& audioBuffer, int32 sampleCount, int32 startIndex = 0)
{
	const double epsilon = 1e-10f; // under -200dB...

	sampleCount += startIndex;
	for (int32 channelIndex = 0; channelIndex < audioBuffer.numChannels; ++channelIndex)
	{
		if (!audioBuffer.channelBuffers64[channelIndex])
			return true;

		for (int32 sampleIndex = startIndex; sampleIndex < sampleCount; ++sampleIndex)
		{
			double val = audioBuffer.channelBuffers64[channelIndex][sampleIndex];
			if (std::abs (val) > epsilon)
				return false;
		}
	}

	return true;
}

//------------------------------------------------------------------------
//------------------------------------------------------------------------
template <typename T>
inline void foreach (IEventList* eventList, const T& func)
{
	if (!eventList)
		return;

	auto eventCount = eventList->getEventCount ();
	for (int32 eventIndex = 0; eventIndex < eventCount; ++eventIndex)
	{
		Vst::Event event = {};
		if (eventList->getEvent (eventIndex, event) != kResultOk)
			continue;

		func (event);
	}
}

//------------------------------------------------------------------------
template <typename T>
inline void foreach (IParamValueQueue& paramQueue, const T& func)
{
	auto paramId = paramQueue.getParameterId ();
	auto numPoints = paramQueue.getPointCount ();
	for (int32 pointIndex = 0; pointIndex < numPoints; ++pointIndex)
	{
		int32 sampleOffset = 0;
		ParamValue value = 0;
		if (paramQueue.getPoint (pointIndex, sampleOffset, value) != kResultOk)
			continue;

		func (paramId, sampleOffset, value);
	}
}

//------------------------------------------------------------------------
template <typename T>
inline void foreachLast (IParamValueQueue& paramQueue, const T& func)
{
	auto paramId = paramQueue.getParameterId ();
	auto numPoints = paramQueue.getPointCount ();
	int32 sampleOffset = 0;
	ParamValue value = 0;
	if (paramQueue.getPoint (numPoints - 1, sampleOffset, value) == kResultOk)
		func (paramId, sampleOffset, value);
}

//------------------------------------------------------------------------
template <typename T>
inline void foreach (IParameterChanges* changes, const T& func)
{
	if (!changes)
		return;

	auto paramCount = changes->getParameterCount ();
	for (int32 paramIndex = 0; paramIndex < paramCount; ++paramIndex)
	{
		auto paramValueQueue = changes->getParameterData (paramIndex);
		if (!paramValueQueue)
			continue;

		func (*paramValueQueue);
	}
}

//------------------------------------------------------------------------
} // namespace Algo
} // namespace Vst
} // namespace Steinberg
