//-----------------------------------------------------------------------------
// Flags       : clang-format SMTGSequencer
// Project     : VST SDK
//
// Category    : Validator
// Filename    : public.sdk/source/vst/testsuite/testbase.cpp
// Created by  : Steinberg, 04/2005
// Description : VST Test Suite
//
//-----------------------------------------------------------------------------
// 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.
//-----------------------------------------------------------------------------

#include "public.sdk/source/vst/testsuite/testbase.h"
#include "public.sdk/source/vst/utility/stringconvert.h"
#include <cstdarg>

//------------------------------------------------------------------------
namespace Steinberg {
namespace Vst {

//------------------------------------------------------------------------
// TestBase
//------------------------------------------------------------------------
TestBase::TestBase (ITestPlugProvider* plugProvider)
: plugProvider (plugProvider)
, vstPlug (nullptr)
, controller (nullptr) {FUNKNOWN_CTOR}

//------------------------------------------------------------------------
TestBase::TestBase ()
: plugProvider (nullptr)
, vstPlug (nullptr)
, controller (nullptr) {FUNKNOWN_CTOR}

//------------------------------------------------------------------------
TestBase::~TestBase () {FUNKNOWN_DTOR}

//------------------------------------------------------------------------
IMPLEMENT_FUNKNOWN_METHODS (TestBase, ITest, ITest::iid);

//------------------------------------------------------------------------
bool TestBase::setup ()
{
	if (plugProvider)
	{
		vstPlug = plugProvider->getComponent ();
		if (!vstPlug)
			return false;
		controller = plugProvider->getController ();

		return activateMainIOBusses (true);
	}
	return false;
}

//------------------------------------------------------------------------
bool TestBase::teardown ()
{
	if (vstPlug)
	{
		activateMainIOBusses (false);
		plugProvider->releasePlugIn (vstPlug, controller);
	}
	return true;
}

//------------------------------------------------------------------------
bool TestBase::activateMainIOBusses (bool val)
{
	if (!vstPlug)
		return false;

	bool result = true;
	if (auto countIn = vstPlug->getBusCount (kAudio, kInput) > 0)
	{
		if (vstPlug->activateBus (kAudio, kInput, 0, val) == kResultFalse)
			result = false;
	}
	if (auto countOut = vstPlug->getBusCount (kAudio, kOutput) > 0)
	{
		if (vstPlug->activateBus (kAudio, kOutput, 0, val) == kResultFalse)
			result = false;
	}

	return result;
}

//------------------------------------------------------------------------
void TestBase::printTestHeader (ITestResult* testResult)
{
	using StringConvert::convert;

	std::string str = "===";
	str += getName ();
	str += " ====================================";
	addMessage (testResult, convert (str));
}

//------------------------------------------------------------------------
// Component Initialize / Terminate
//------------------------------------------------------------------------

//------------------------------------------------------------------------
// VstTestEnh
//------------------------------------------------------------------------
TestEnh::TestEnh (ITestPlugProvider* plugProvider, ProcessSampleSize sampl)
: TestBase (plugProvider), audioEffect (nullptr)
{
	// process setup defaults
	memset (&processSetup, 0, sizeof (ProcessSetup));

	processSetup.processMode = kRealtime;
	processSetup.symbolicSampleSize = sampl;
	processSetup.maxSamplesPerBlock = kMaxSamplesPerBlock;
	processSetup.sampleRate = kSampleRate;
}

//------------------------------------------------------------------------
TestEnh::~TestEnh ()
{
}

//------------------------------------------------------------------------
bool TestEnh::setup ()
{
	bool res = TestBase::setup ();

	if (vstPlug)
	{
		tresult check = vstPlug->queryInterface (IAudioProcessor::iid, (void**)&audioEffect);
		if (check != kResultTrue)
			return false;
	}

	return (res && audioEffect);
}

//------------------------------------------------------------------------
bool TestEnh::teardown ()
{
	if (audioEffect)
		audioEffect->release ();

	bool res = TestBase::teardown ();

	return res && audioEffect;
}

//------------------------------------------------------------------------
void addMessage (ITestResult* testResult, const std::u16string& str)
{
	testResult->addMessage (reinterpret_cast<const tchar*> (str.data ()));
}

//------------------------------------------------------------------------
void addMessage (ITestResult* testResult, const tchar* str)
{
	testResult->addMessage (str);
}

//------------------------------------------------------------------------
void addErrorMessage (ITestResult* testResult, const tchar* str)
{
	testResult->addErrorMessage (str);
}

//------------------------------------------------------------------------
void addErrorMessage (ITestResult* testResult, const std::u16string& str)
{
	testResult->addErrorMessage (reinterpret_cast<const tchar*> (str.data ()));
}

//------------------------------------------------------------------------
std::u16string printf (const char8* format, ...)
{
	using StringConvert::convert;

	char8 string[1024 * 4];

	va_list marker;
	va_start (marker, format);

	vsnprintf (string, kPrintfBufferSize, format, marker);
	return convert (string);
}

IMPLEMENT_FUNKNOWN_METHODS (ParamChanges, IParamValueQueue, IParamValueQueue::iid)

//------------------------------------------------------------------------
ParamChanges::ParamChanges () {FUNKNOWN_CTOR}

//------------------------------------------------------------------------
ParamChanges::~ParamChanges ()
{
	if (points)
		delete[] points;
	FUNKNOWN_DTOR
}

//------------------------------------------------------------------------
void ParamChanges::init (ParamID _id, int32 _numPoints)
{
	id = _id;
	numPoints = _numPoints;
	numUsedPoints = 0;
	if (points)
		delete[] points;
	points = new ParamPoint[numPoints];
	processedFrames = 0;
}

//------------------------------------------------------------------------
bool ParamChanges::setPoint (int32 index, int32 offsetSamples, double value)
{
	if (points && (index >= 0) && (index == numUsedPoints) && (index < numPoints))
	{
		points[index].set (offsetSamples, value);
		numUsedPoints++;
		return true;
	}
	if (!points)
		return true;
	return false;
}

//------------------------------------------------------------------------
void ParamChanges::resetPoints ()
{
	numUsedPoints = 0;
	processedFrames = 0;
}

//------------------------------------------------------------------------
int32 ParamChanges::getProcessedFrames () const
{
	return processedFrames;
}

//------------------------------------------------------------------------
void ParamChanges::setProcessedFrames (int32 amount)
{
	processedFrames = amount;
}

//------------------------------------------------------------------------
bool ParamChanges::havePointsBeenRead (bool atAll)
{
	for (int32 i = 0; i < getPointCount (); ++i)
	{
		if (points[i].wasRead ())
		{
			if (atAll)
				return true;
		}
		else if (!atAll)
			return false;
	}
	return !atAll;
}

//------------------------------------------------------------------------
ParamID PLUGIN_API ParamChanges::getParameterId ()
{
	return id;
}

//------------------------------------------------------------------------
int32 PLUGIN_API ParamChanges::getPointCount ()
{
	return numUsedPoints;
}

//------------------------------------------------------------------------
tresult PLUGIN_API ParamChanges::getPoint (int32 index, int32& offsetSamples, double& value)
{
	if (points && (index < numUsedPoints) && (index >= 0))
	{
		points[index].get (offsetSamples, value);
		return kResultTrue;
	}
	return kResultFalse;
}

//------------------------------------------------------------------------
tresult PLUGIN_API ParamChanges::addPoint (int32 /*offsetSamples*/, double /*value*/,
                                           int32& /*index*/)
{
	return kResultFalse;
}

//------------------------------------------------------------------------
} // Vst
} // Steinberg
