#include "CAlgorithmGlobalDefinesGen.hpp"

#include <iostream>
#include <functional>

namespace OpenViBE {
namespace PluginInspector {

CAlgorithmGlobalDefinesGen::CAlgorithmGlobalDefinesGen(const Kernel::IKernelContext& ctx, const std::string& filename)
	: CPluginObjectDescEnum(ctx)
{
	m_usedIDs.clear();

	const std::string fullFilename = filename + "/ovp_global_defines.h";

	CPluginObjectDescEnum::getLogManager() << Kernel::LogLevel_Info << "Writing global defines to '" << fullFilename << "\n";

	m_file.open(fullFilename.c_str());

	OV_ERROR_UNLESS_K(m_file.good(), "Error opening '" << fullFilename << "for writing.", Kernel::ErrorType::BadFileRead,)
	; // Please note the ",)" to simulate "return;"

	m_file << "#pragma once\n\n// This file was automatically generated by Plugin Inspector\n\n";
}

CAlgorithmGlobalDefinesGen::~CAlgorithmGlobalDefinesGen()
{
	if (!m_file.good()) { return; }

	m_file << "\n";
	m_file.close();
}

void CAlgorithmGlobalDefinesGen::addIdentifier(const std::string& objectName, const CIdentifier& candidate, const std::string& spaces)
{
	if (m_usedIDs.find(candidate) == m_usedIDs.end()) {
		m_usedIDs[candidate] = objectName;
		m_file << "#define " << objectName << spaces << "CIdentifier" << candidate.str() << "\n";
	}
	else {
		OV_WARNING_K("    Object '" << objectName << "' has duplicate id " << candidate.str() << " with " << m_usedIDs[candidate]) ;
		m_file << "#define " << objectName << spaces << "CIdentifier" << candidate.str() << " // Duplicate of " << m_usedIDs[candidate] << "\n";
	}
}

bool CAlgorithmGlobalDefinesGen::Callback(const Plugins::IPluginObjectDesc& pluginObjectDesc)
{
	OV_ERROR_UNLESS_KRF(m_file.good(), "Cannot use file; Was it opened correctly ?", Kernel::ErrorType::BadProcessing);

	int width = 120;

	getLogManager() << Kernel::LogLevel_Info << "  Dumping [" << pluginObjectDesc.getName() << "]\n";

	Kernel::IAlgorithmManager& manager = m_kernelCtx.getAlgorithmManager();
	const CIdentifier algorithmId      = manager.createAlgorithm(pluginObjectDesc.getCreatedClass());
	OV_ERROR_UNLESS_KRF(algorithmId != CIdentifier::undefined(), "  Unable to create algorithm", Kernel::ErrorType::BadProcessing);

	Kernel::IAlgorithmProxy& proxy = manager.getAlgorithm(algorithmId);

	m_file << "// -----------------------------------------------------\n";
	m_file << "// " << pluginObjectDesc.getName().toASCIIString() << "\n";
	m_file << "// -----------------------------------------------------\n";
	m_file << "\n";

	proxy.initialize();

	const auto addIdentifierFromParameters = [&width, &pluginObjectDesc, this](const char* nameStart, const char* nameEnd, const CIdentifier& id,
																			   const std::string& toAppend)
	{
		std::string defName;
		defName += nameStart;
		defName += Transform(pluginObjectDesc.getName().toASCIIString(), true);
		defName += nameEnd;
		if (!toAppend.empty()) { defName += Transform(toAppend, true); }

		std::string spaces;
		spaces.resize(width - defName.length(), ' ');

		addIdentifier(defName, id, spaces);
	};

	addIdentifierFromParameters("OVP_GD_ClassId_Algorithm_", "Desc", pluginObjectDesc.getCreatedClassIdentifier(), "");

	CIdentifier id = CIdentifier::undefined();
	while ((id = proxy.getNextInputParameterIdentifier(id)) != CIdentifier::undefined()) { addIdentifierFromParameters("OVP_GD_Algorithm_", "_InputParameterId_", id, proxy.getInputParameterName(id).toASCIIString()); }

	id = CIdentifier::undefined();
	while ((id = proxy.getNextOutputParameterIdentifier(id)) != CIdentifier::undefined()) { addIdentifierFromParameters("OVP_GD_Algorithm_", "_OutputParameterId_", id, proxy.getOutputParameterName(id).toASCIIString()); }

	id = CIdentifier::undefined();
	while ((id = proxy.getNextInputTriggerIdentifier(id)) != CIdentifier::undefined()) { addIdentifierFromParameters("OVP_GD_Algorithm_", "_InputTriggerId_", id, proxy.getInputTriggerName(id).toASCIIString()); }

	id = CIdentifier::undefined();
	while ((id = proxy.getNextOutputTriggerIdentifier(id)) != CIdentifier::undefined()) { addIdentifierFromParameters("OVP_GD_Algorithm_", "_OutputTriggerId_", id, proxy.getOutputTriggerName(id).toASCIIString()); }

	proxy.uninitialize();

	m_file << "\n";

	manager.releaseAlgorithm(proxy);

	return true;
}

}  // namespace PluginInspector
}  // namespace OpenViBE
