#pragma once

/// @file HARKlib.h
/// @brief HARK Library を Windows/C# 環境から利用するためのラッパーを定義する

using namespace System;

#include "hark-lib/network.h"

//#include "hark-lib/node/AND.h"
#include "hark-lib/node/Constant.h"
//#include "hark-lib/node/Delay.h"
//#include "hark-lib/node/Equal.h"
//#include "hark-lib/node/Grater.h"
//#include "hark-lib/node/IF.h"
#include "hark-lib/node/InputStream.h"
//#include "hark-lib/node/IterCount.h"
//#include "hark-lib/node/Iterate.h"
//#include "hark-lib/node/NOP.h"
//#include "hark-lib/node/NOT.h"
//#include "hark-lib/node/OR.h"
//#include "hark-lib/node/Sleep.h"
//#include "hark-lib/node/Smaller.h"

#include "hark-lib/node/AudioStreamFromMemory.h"
#include "hark-lib/node/AudioStreamFromWave.h"
//#include "hark-lib/node/BF.h"
//#include "hark-lib/node/BGNEstimator.h"
//#include "hark-lib/node/CMChannelSelector.h"
//#include "hark-lib/node/CMConjEachElement.h"
//#include "hark-lib/node/CMDivideEachElement.h"
//#include "hark-lib/node/CMIdentityMatrix.h"
//#include "hark-lib/node/CMInverseMatrix.h"
//#include "hark-lib/node/CMLoad.h"
//#include "hark-lib/node/CMMakerFromFFT.h"
//#include "hark-lib/node/CMMakerFromFFTwithFlag.h"
//#include "hark-lib/node/CMMultiplyEachElement.h"
//#include "hark-lib/node/CMMultiplyMatrix.h"
//#include "hark-lib/node/CMSave.h"
//#include "hark-lib/node/CSP.h"
//#include "hark-lib/node/CalcSpecAddPower.h"
//#include "hark-lib/node/CalcSpecSubGain.h"
#include "hark-lib/node/ChannelSelector.h"
//#include "hark-lib/node/CombineMap.h"
//#include "hark-lib/node/CombineSource.h"
//#include "hark-lib/node/ConstantLocalization.h"
//#include "hark-lib/node/DataLogger.h"
#include "hark-lib/node/Delta.h"
//#include "hark-lib/node/DeltaMask.h"
//#include "hark-lib/node/DeltaPowerMask.h"
#include "hark-lib/node/DisplayLocalization.h"
//#include "hark-lib/node/EstimateLeak.h"
//#include "hark-lib/node/EstimateTF.h"
#include "hark-lib/node/FeatureRemover.h"
//#include "hark-lib/node/GHDSS.h"
//#include "hark-lib/node/HRLE.h"
//#include "hark-lib/node/HarkDataStreamSender.h"
//#include "hark-lib/node/HarkParamDynReconf.h"
//#include "hark-lib/node/LoadMapFrames.h"
//#include "hark-lib/node/LoadSourceFrames.h"
//#include "hark-lib/node/LoadVectorFrames.h"
//#include "hark-lib/node/LocalizeBFDS.h"
#include "hark-lib/node/LocalizeMUSIC.h"
//#include "hark-lib/node/MFCCExtraction.h"
//#include "hark-lib/node/MFMGeneration.h"
//#include "hark-lib/node/ML.h"
#include "hark-lib/node/MSLSExtraction.h"
//#include "hark-lib/node/MSNR.h"
//#include "hark-lib/node/MVDR.h"
//#include "hark-lib/node/MapIDOffset.h"
//#include "hark-lib/node/MapMatrixValueOverwrite.h"
//#include "hark-lib/node/MapOperator.h"
//#include "hark-lib/node/MapSelectorBySource.h"
//#include "hark-lib/node/MapToMap.h"
//#include "hark-lib/node/MapToMatrix.h"
//#include "hark-lib/node/MapToVector.h"
//#include "hark-lib/node/MapVectorValueOverwrite.h"
//#include "hark-lib/node/MatrixToMap.h"
//#include "hark-lib/node/MatrixToMatrix.h"
//#include "hark-lib/node/MatrixToVector.h"
//#include "hark-lib/node/MatrixValueOverwrite.h"
#include "hark-lib/node/MelFilterBank.h"
//#include "hark-lib/node/MultiDownSampler.h"
#include "hark-lib/node/MultiFFT.h"
#include "hark-lib/node/MultiGain.h"
//#include "hark-lib/node/MultiLocalizationIntegrator.h"
//#include "hark-lib/node/NormalizeMUSIC.h"
//#include "hark-lib/node/PlayAudio.h"
//#include "hark-lib/node/PostFilter.h"
//#include "hark-lib/node/PowerCalcForMap.h"
//#include "hark-lib/node/PowerCalcForMatrix.h"
#include "hark-lib/node/PreEmphasis.h"
//#include "hark-lib/node/ResizeMapMatrixValue.h"
//#include "hark-lib/node/ResizeMapVectorValue.h"
#include "hark-lib/node/SaveFeatures.h"
//#include "hark-lib/node/SaveHTKFeatures.h"
//#include "hark-lib/node/SaveMapFrames.h"
//#include "hark-lib/node/SaveMatrixFrames.h"
//#include "hark-lib/node/SaveRawPCM.h"
//#include "hark-lib/node/SaveSourceLocation.h"
//#include "hark-lib/node/SaveVectorFrames.h"
#include "hark-lib/node/SaveWavePCM.h"
//#include "hark-lib/node/SegmentAudioStreamByID.h"
#include "hark-lib/node/SemiBlindICA.h"
//#include "hark-lib/node/SourceIntervalExtender.h"
//#include "hark-lib/node/SourceSelectorByDirection.h"
//#include "hark-lib/node/SourceSelectorByID.h"
//#include "hark-lib/node/SourceSelectorBySourceInfo.h"
#include "hark-lib/node/SourceTracker.h"
//#include "hark-lib/node/SourceTrackerPF.h"
//#include "hark-lib/node/SourceTransformer.h"
//#include "hark-lib/node/SpectralGainFilter.h"
//#include "hark-lib/node/SpectralMeanNormalization.h"
#include "hark-lib/node/SpectralMeanNormalizationIncremental.h"
#include "hark-lib/node/SpeechRecognitionClient.h"
//#include "hark-lib/node/SpeechRecognitionSMNClient.h"
#include "hark-lib/node/SubscriberFLAC.h"
#include "hark-lib/node/Synthesize.h"
//#include "hark-lib/node/TextConcatenate.h"
//#include "hark-lib/node/TextConverter.h"
#include "hark-lib/node/VADZC.h"
#include "hark-lib/node/VectorToMap.h"
#include "hark-lib/node/VectorToMatrix.h"
#include "hark-lib/node/VectorToVector.h"
#include "hark-lib/node/VectorValueOverwrite.h"
#include "hark-lib/node/WhiteNoiseAdder.h"

#include "hark-lib/node/Print.h"
#include "hark-lib/node/PublishData.h"
#include "hark-lib/node/SubscribeData.h"

using namespace System::Runtime::InteropServices;

namespace HARKlib {
	ref class Node;
	ref class NodeDef;
	ref class Network;
	ref class NetworkDef;
	ref class DataSource;
	ref class DataSink;
	ref class DataSourceMap;
	ref class DataSinkMap;
	ref class BaseDispatcher;

	public ref class Source {
	public:
		Source(int id, float x, float y, float z, int sub_id, int mic_id, float power)
			: id(id), x(x), y(y), z(z), sub_id(sub_id), mic_id(mic_id), power(power)
		{}

		String^ ToString() override {
			return String::Format("Source: pos=({0}, {1}, {2}), id={3}, sub_id={4}, mic_id={5}, power={6}", this->x, this->y, this->z, this->id, this->sub_id, this->mic_id, this->power);
		}

	public:
		int   id, sub_id, mic_id;
		float x, y, z, power;
	};

	static std::string StringToStdString(String^ src)
	{
		char* str2 = (char*)(void*)Marshal::StringToHGlobalAnsi(src);
		std::string dst = str2;
		Marshal::FreeHGlobal((System::IntPtr)str2);
		return dst;
	}

	template<typename N, typename D>
	class BaseUserData : public hark::BaseNode::UserData
	{
	public:
		BaseUserData(N^ n, D^ def) :
			_n(n), _def(def)
		{
		}

		virtual void clean() {
			this->_n = nullptr;
			this->_def = nullptr;
		}

		N^ get_n() const { return this->_n; }
		D^ get_def() const { return this->_def; }

	private:
		gcroot<N^> _n;
		gcroot<D^> _def;
	};

	using NodeUserData = BaseUserData<Node, NodeDef>;
	using NetworkUserData = BaseUserData<Network, NetworkDef>;

	public ref class BaseDispatcher {
	public:
		BaseDispatcher(hark::dispatch::BaseDispatcher* dispatcher) :
			_inner(dispatcher)
		{}

		hark::dispatch::BaseDispatcher* inner() { return this->_inner; }

	protected:
		hark::dispatch::BaseDispatcher* _inner;
	};

	public ref class OneShotDispatcher : public BaseDispatcher {
	public:
		OneShotDispatcher() : BaseDispatcher(new hark::dispatch::OneShot()) {
		}
	};
	public ref class RepeatDispatcher : public BaseDispatcher {
	public:
		RepeatDispatcher() : BaseDispatcher(new hark::dispatch::Repeat()) {
		}
	};
	public ref class TriggeredOneShotDispatcher : public BaseDispatcher {
	public:
		TriggeredOneShotDispatcher() : BaseDispatcher(new hark::dispatch::TriggeredOneShot()) {
		}
	};
	public ref class TriggeredMultiShotDispatcher : public BaseDispatcher {
	public:
		TriggeredMultiShotDispatcher() : BaseDispatcher(new hark::dispatch::TriggeredMultiShot()) {
		}
	};
	public ref class TriggeredRepeatDispatcher : public BaseDispatcher {
	public:
		TriggeredRepeatDispatcher() : BaseDispatcher(new hark::dispatch::TriggeredRepeat()) {
		}
	};

	public ref class DataSource {
	public:
		DataSource(hark::DataSource* data_source) :
			_inner(data_source)
		{}

	public:
		hark::DataSource* inner() { return this->_inner; }

	protected:
		hark::DataSource* _inner;
	};

	public ref class DataSink {
	};

	/**
		@class NodeDef
		@brief ノードの実体を管理するクラス。

		NodeDef と Node は 1:N の関係にある。
	*/
	public ref class NodeDef {
	public:
		NodeDef(hark::NodeDef* nodedef) :
			_inner(nodedef)
		{}

		hark::NodeDef* inner() { return this->_inner; }

	protected:
		hark::NodeDef* _inner;

	};

	class NetworkDefWrapper : public hark::NetworkDef
	{
		gcroot<HARKlib::NetworkDef^> _def;

	public:
		NetworkDefWrapper(HARKlib::NetworkDef^ def) :
			_def(def)
		{
		}

		virtual void build(hark::Network& network, hark::DataSourceMap& input, hark::DataSinkMap& output);
	};

	/**
		@class NetworkDef
		@brief ネットワークの構成を定義するためのクラス。

	*/
	public ref class NetworkDef {
	public:
		NetworkDef() :
			_inner(new NetworkDefWrapper(this))
		{}

	public:
		/**
			ネットワークの構成を定義するために HARK Library から呼ばれるメソッド。
			ユーザは、このメソッドを override して、ネットワークを構成する処理を記述する。

			@param network ネットワークオブジェクト
			@param input ネットワークの入力を束ねた DataSourceMap
			@param output ネットワークの出力を束ねた DataSinkMap
		*/
		virtual void build(Network^ network, DataSourceMap^ input, DataSinkMap^ output) {}

		hark::NetworkDef* inner() { return this->_inner; }

	protected:
		hark::NetworkDef* _inner;
	};

	/**
		@class Node
		@brief ノードの実体を管理するクラス。

		NodeDef と Node は 1:N の関係にある。
	*/
	public ref class Node {
	public:
		Node(hark::BaseNode* inner) :
			_node(inner)
		{

		}

	public:
		Node^ add_input(String^ name, DataSource^ source)
		{

			this->_node->add_input(hark::Input(StringToStdString(name), *source->inner()));
			return this;
		}

		Node^ add_input(String^ name, String^ value)
		{
			this->_node->add_input(hark::Input(StringToStdString(name), StringToStdString(value)));
			return this;
		}

		Node^ add_input(String^ name, int value)
		{
			this->_node->add_input(hark::Input(StringToStdString(name), value));
			return this;
		}

		Node^ add_input(String^ name, double value)
		{
			this->_node->add_input(hark::Input(StringToStdString(name), value));
			return this;
		}

		Node^ add_input(String^ name, bool value)
		{
			this->_node->add_input(hark::Input(StringToStdString(name), value));
			return this;
		}

		property DataSource^ default[String^]{
			DataSource ^ get(String ^ index)
			{
				hark::DataSource& source = this->_node->get(StringToStdString(index));
				return gcnew DataSource(&source);
			}
		}

	private:
		hark::BaseNode* _node;

	};

	/**
		@class Network
		@brief ネットワークの実体を管理するためのクラス。

	*/
	public ref class Network {
	public:
		Network(hark::Network *network) :
			_network(network)
		{

		}

		~Network()
		{
			delete this->_network;
		}

		/**
			指定したノード定義を用いて、ノードを構築する。

			@tparam ND ノード定義クラス
		*/
		generic<typename ND>
			where ND: NodeDef, gcnew()
		Node^ create() {
			return this->_create_node< ND, HARKlib::TriggeredOneShotDispatcher^ > ("Node");
		}

		generic<typename ND>
			where ND: NodeDef, gcnew()
		Node^ create(String^ name) {
			return this->_create_node < ND, HARKlib::TriggeredOneShotDispatcher^ >(name);
		}

		generic<typename ND, typename DP>
			where ND: NodeDef, gcnew()
			where DP : HARKlib::BaseDispatcher, gcnew()
		Node^ create() {
			return this->_create_node < ND,DP >("Node");
		}

		generic<typename ND, typename DP>
			where ND : NodeDef, gcnew()
			where DP : BaseDispatcher, gcnew()
		Node^ create(String^ name) {
			return this->_create_node<ND, DP>(name);
		}

		generic<typename ND>
			where ND: NetworkDef, gcnew()
		Network^ create() {
			return this->_create_network < ND, HARKlib::TriggeredOneShotDispatcher^ >("Node");
		}

		generic<typename ND>
			where ND: NetworkDef, gcnew()
		Network^ create(String^ name) {
			return this->_create_network < ND, HARKlib::TriggeredOneShotDispatcher^ >(name);
		}

		generic<typename ND, typename DP>
			where ND: NetworkDef, gcnew()
			where DP : HARKlib::BaseDispatcher, gcnew()
		Network^ create() {
			return this->_create_network < ND, DP >("Node");
		}

		generic<typename ND, typename DP>
			where ND: NetworkDef, gcnew()
			where DP : BaseDispatcher, gcnew()
		Network^ create(String^ name) {
			return this->_create_network<ND, DP>(name);
		}

		generic<typename ND>
			where ND: NetworkDef, gcnew()
		static Network^ create_network(String^ name) {
			NetworkDef^ networkdef = gcnew ND();
			hark::Network* network = new hark::Network(networkdef->inner(), StringToStdString(name));
			Network^ n = gcnew Network(network);
			network->attach_userdata(new NetworkUserData(n, networkdef));
			return n;
		}

		NodeDef^ query_nodedef(String^ name) {
			hark::BaseNode* node = this->_network->query_node(StringToStdString(name));
			if (node) {
				const hark::BaseNode::UserData* ud = node->get_userdata();
				if (ud) {
					const NodeUserData* node_ud = dynamic_cast<const NodeUserData*>(ud);
					if (node_ud) {
						return node_ud->get_def();
					}
				}
			}
			return nullptr;
		}

		Network^ add_input(String^ name, DataSource^ source)
		{

			this->_network->add_input(hark::Input(StringToStdString(name), *source->inner()));
			return this;
		}

		Network^ add_input(String^ name, String^ value)
		{
			this->_network->add_input(hark::Input(StringToStdString(name), StringToStdString(value)));
			return this;
		}

		Network^ add_input(String^ name, int value)
		{
			this->_network->add_input(hark::Input(StringToStdString(name), value));
			return this;
		}

		Network^ add_input(String^ name, double value)
		{
			this->_network->add_input(hark::Input(StringToStdString(name), value));
			return this;
		}

		Network^ add_input(String^ name, bool value)
		{
			this->_network->add_input(hark::Input(StringToStdString(name), value));
			return this;
		}

		property DataSource^ default[String^]{
			DataSource ^ get(String ^ index)
			{
				hark::DataSource& source = this->_network->get(StringToStdString(index));
				return gcnew DataSource(&source);
			}
		}

		void execute()
		{
			this->_network->execute();
		}

		void stop()
		{
			this->_network->stop();
		}

	protected:
		generic<typename ND, typename DP>
			where ND : NodeDef, gcnew()
			where DP : BaseDispatcher, gcnew()
		Node^ _create_node(String^ name) {
			NodeDef^ nodedef = gcnew ND();
			BaseDispatcher^ dispatcher = gcnew DP();

			hark::BaseNode& native_node = this->_network->create_node(nodedef->inner(), dispatcher->inner(), StringToStdString(name));
			Node^ node = gcnew Node(&native_node);
			native_node.attach_userdata(new NodeUserData(node, nodedef));

			return node;
		}

		generic<typename ND, typename DP>
			where ND: NetworkDef, gcnew()
			where DP : BaseDispatcher, gcnew()
		Network^ _create_network(String^ name) {
			NetworkDef^ networkdef = gcnew ND();
			BaseDispatcher^ dispatcher = gcnew DP();

			hark::Network& native_network = this->_network->create_network(networkdef->inner(), dispatcher->inner(), "Network");
			Network^ network = gcnew Network(&native_network);
			native_network.attach_userdata(new NetworkUserData(network, networkdef));

			return network;
		}

	private:
		hark::Network* _network;
	};


	public ref class DataSinkMap {
	public:
		DataSinkMap(hark::DataSinkMap* data_sink_map) :
			_inner(data_sink_map)
		{}

	public:
		DataSinkMap^ add_input(String^ name, DataSource^ source)
		{
			this->_inner->operator[] (StringToStdString(name)) << *source->inner();
			return this;
		}

		//property DataSink^ default[String^]{
		//	DataSink^ get(String ^ index)
		//	{
		//		return nullptr;
		//	}
		//}

		hark::DataSinkMap* inner() { return this->_inner; }

	protected:
		hark::DataSinkMap* _inner;
	};

	public ref class DataSourceMap {
	public:
		DataSourceMap(hark::DataSourceMap* data_source_map) :
			_inner(data_source_map)
		{}

	public:
		DataSourceMap^ add_input(String^ name, DataSource^ source) { return this; }

		property DataSource^ default[String^]{
			DataSource ^ get(String ^ index) {
				char* str = (char*)(void*)Marshal::StringToHGlobalAnsi(index);
				hark::DataSource& ds = this->_inner->get(str);
				Marshal::FreeHGlobal((System::IntPtr)str);

				return gcnew DataSource(&ds);
			}
		}

		hark::DataSourceMap* inner() { return this->_inner; }

	protected:
		hark::DataSourceMap* _inner;
	};

#define DEFINE_NODEDEF(name) \
	public ref class name : public NodeDef { \
	public: \
		name() : NodeDef(new hark::node::name()) {} \
	}; \

	//DEFINE_NODEDEF(AND);
	//DEFINE_NODEDEF(Constant);
	//DEFINE_NODEDEF(Delay);
	//DEFINE_NODEDEF(Equal);
	//DEFINE_NODEDEF(Grater);
	//DEFINE_NODEDEF(IF);
	//DEFINE_NODEDEF(InputStream);
	//DEFINE_NODEDEF(IterCount);
	//DEFINE_NODEDEF(Iterate);
	//DEFINE_NODEDEF(NOP);
	//DEFINE_NODEDEF(NOT);
	//DEFINE_NODEDEF(OR);
	//DEFINE_NODEDEF(Sleep);
	//DEFINE_NODEDEF(Smaller);

	DEFINE_NODEDEF(AudioStreamFromMemory);
	DEFINE_NODEDEF(AudioStreamFromWave);
	//DEFINE_NODEDEF(BF);
	//DEFINE_NODEDEF(BGNEstimator);
	//DEFINE_NODEDEF(CMChannelSelector);
	//DEFINE_NODEDEF(CMConjEachElement);
	//DEFINE_NODEDEF(CMDivideEachElement);
	//DEFINE_NODEDEF(CMIdentityMatrix);
	//DEFINE_NODEDEF(CMInverseMatrix);
	//DEFINE_NODEDEF(CMLoad);
	//DEFINE_NODEDEF(CMMakerFromFFT);
	//DEFINE_NODEDEF(CMMakerFromFFTwithFlag);
	//DEFINE_NODEDEF(CMMultiplyEachElement);
	//DEFINE_NODEDEF(CMMultiplyMatrix);
	//DEFINE_NODEDEF(CMSave);
	//DEFINE_NODEDEF(CSP);
	//DEFINE_NODEDEF(CalcSpecAddPower);
	//DEFINE_NODEDEF(CalcSpecSubGain);
	//DEFINE_NODEDEF(ChannelSelector);
	//DEFINE_NODEDEF(CombineMap);
	//DEFINE_NODEDEF(CombineSource);
	//DEFINE_NODEDEF(ConstantLocalization);
	//DEFINE_NODEDEF(DataLogger);
	DEFINE_NODEDEF(Delta);
	//DEFINE_NODEDEF(DeltaMask);
	//DEFINE_NODEDEF(DeltaPowerMask);
	//DEFINE_NODEDEF(DisplayLocalization);
	//DEFINE_NODEDEF(EstimateLeak);
	//DEFINE_NODEDEF(EstimateTF);
	DEFINE_NODEDEF(FeatureRemover);
	//DEFINE_NODEDEF(GHDSS);
	//DEFINE_NODEDEF(HRLE);
	//DEFINE_NODEDEF(HarkDataStreamSender);
	//DEFINE_NODEDEF(HarkParamDynReconf);
	//DEFINE_NODEDEF(LoadMapFrames);
	//DEFINE_NODEDEF(LoadSourceFrames);
	//DEFINE_NODEDEF(LoadVectorFrames);
	//DEFINE_NODEDEF(LocalizeBFDS);
	//DEFINE_NODEDEF(LocalizeMUSIC);
	//DEFINE_NODEDEF(MFCCExtraction);
	//DEFINE_NODEDEF(MFMGeneration);
	//DEFINE_NODEDEF(ML);
	DEFINE_NODEDEF(MSLSExtraction);
	//DEFINE_NODEDEF(MSNR);
	//DEFINE_NODEDEF(MVDR);
	//DEFINE_NODEDEF(MapIDOffset);
	//DEFINE_NODEDEF(MapMatrixValueOverwrite);
	//DEFINE_NODEDEF(MapOperator);
	//DEFINE_NODEDEF(MapSelectorBySource);
	//DEFINE_NODEDEF(MapToMap);
	//DEFINE_NODEDEF(MapToMatrix);
	//DEFINE_NODEDEF(MapToVector);
	//DEFINE_NODEDEF(MapVectorValueOverwrite);
	//DEFINE_NODEDEF(MatrixToMap);
	//DEFINE_NODEDEF(MatrixToMatrix);
	//DEFINE_NODEDEF(MatrixToVector);
	//DEFINE_NODEDEF(MatrixValueOverwrite);
	DEFINE_NODEDEF(MelFilterBank);
	//DEFINE_NODEDEF(MultiDownSampler);
	DEFINE_NODEDEF(MultiFFT);
	//DEFINE_NODEDEF(MultiGain);
	//DEFINE_NODEDEF(MultiLocalizationIntegrator);
	//DEFINE_NODEDEF(NormalizeMUSIC);
	//DEFINE_NODEDEF(PlayAudio);
	//DEFINE_NODEDEF(PostFilter);
	//DEFINE_NODEDEF(PowerCalcForMap);
	//DEFINE_NODEDEF(PowerCalcForMatrix);
	DEFINE_NODEDEF(PreEmphasis);
	//DEFINE_NODEDEF(ResizeMapMatrixValue);
	//DEFINE_NODEDEF(ResizeMapVectorValue);
	DEFINE_NODEDEF(SaveFeatures);
	//DEFINE_NODEDEF(SaveHTKFeatures);
	//DEFINE_NODEDEF(SaveMapFrames);
	//DEFINE_NODEDEF(SaveMatrixFrames);
	//DEFINE_NODEDEF(SaveRawPCM);
	//DEFINE_NODEDEF(SaveSourceLocation);
	//DEFINE_NODEDEF(SaveVectorFrames);
	DEFINE_NODEDEF(SaveWavePCM);
	//DEFINE_NODEDEF(SegmentAudioStreamByID);
	DEFINE_NODEDEF(SemiBlindICA);
	//DEFINE_NODEDEF(SourceIntervalExtender);
	//DEFINE_NODEDEF(SourceSelectorByDirection);
	//DEFINE_NODEDEF(SourceSelectorByID);
	//DEFINE_NODEDEF(SourceSelectorBySourceInfo);
	//DEFINE_NODEDEF(SourceTracker);
	//DEFINE_NODEDEF(SourceTrackerPF);
	//DEFINE_NODEDEF(SourceTransformer);
	//DEFINE_NODEDEF(SpectralGainFilter);
	//DEFINE_NODEDEF(SpectralMeanNormalization);
	DEFINE_NODEDEF(SpectralMeanNormalizationIncremental);
	DEFINE_NODEDEF(SpeechRecognitionClient);
	//DEFINE_NODEDEF(SpeechRecognitionSMNClient);
	//DEFINE_NODEDEF(SubscribeFLAC);
	DEFINE_NODEDEF(Synthesize);
	//DEFINE_NODEDEF(TextConcatenate);
	//DEFINE_NODEDEF(TextConverter);
	DEFINE_NODEDEF(VADZC);
	//DEFINE_NODEDEF(VectorToMap);
	//DEFINE_NODEDEF(VectorToMarix);
	//DEFINE_NODEDEF(VectorToVector);
	//DEFINE_NODEDEF(VectorValueOverwrite);
	//DEFINE_NODEDEF(WhiteNoiseAdder);

	DEFINE_NODEDEF(Print);

	public ref class PublishData : public NodeDef {
	public:
		PublishData() : NodeDef(new hark::node::PublishData()) {
		}

		~PublishData() {
			delete this->_inner;
		}

		void push(cli::array<short>^ data) {
			using System::IntPtr;
			using System::Runtime::InteropServices::Marshal;

			std::vector<short> value(data->Length);
			Marshal::Copy(data, 0, IntPtr(&value[0]), data->Length);

			hark::node::PublishData* publish_data = dynamic_cast<hark::node::PublishData*>(this->_inner);
			publish_data->push(hark::data(value));
		}

	};

	ref class SubscribeData;
	class SubscribeDataWrapper;

	class SubscribeDataWrapper : public hark::node::SubscribeData
	{
	public:
		SubscribeDataWrapper(HARKlib::SubscribeData ^outer) :
			_outer(outer)
		{
		}

		virtual void receive(hark::data output);

	private:
		gcroot<HARKlib::SubscribeData ^> _outer;

	};

	public ref class SubscribeDataEventArgs : public EventArgs {
	public:
		SubscribeDataEventArgs(Object^ received_data) {
			this->ReceivedData = received_data;
		}

		property Object^ ReceivedData;
	};

	public delegate void SubscribeDataDelegate(Object^, SubscribeDataEventArgs^);

	public ref class SubscribeData : public NodeDef {
	public:
		event SubscribeDataDelegate ^Received;

		SubscribeData() : NodeDef(new SubscribeDataWrapper(this)) {
		}

		~SubscribeData() {
			delete this->_inner;
		}

		Object^ object_from_data(hark::data value)
		{
			if (value.is_type<std::map<int, hark::data>>()) {
				Collections::Generic::Dictionary<int, Object^>^ output = gcnew Collections::Generic::Dictionary<int, Object^>();

				const auto& map = value.cast<std::map<int, hark::data>>();
				for (auto it : map) {
					output->Add(it.first, this->object_from_data(it.second));
				}

				return output;

			}
			else if (value.is_type<std::vector<hark::data>>()) {
				Collections::Generic::List<Object^>^ output = gcnew Collections::Generic::List<Object^>();

				const auto& vec = value.cast<std::vector<hark::data>>();
				for (auto it : vec) {
					output->Add(this->object_from_data(it));
				}

				return output;

			}else if (value.is_type<std::vector<float>>()) {
				const auto& vec = value.cast<std::vector<float>>();

				cli::array<float>^ output = gcnew cli::array<float>(vec.size());
				for (size_t i = 0; i < vec.size(); i++) {
					output[i] = vec[i];
				}

				return output;

			}
			else if (value.is_type<hark::Source>()) {
				const auto& src = value.cast<hark::Source>();

				Source^ output = gcnew Source(src.id, src.x[0], src.x[1], src.x[2], src.sub_id, src.mic_id, src.power);
				return output;

			}
			else {
				return nullptr;
			}
		}

		void receive(hark::data output) {
			Object^ value = this->object_from_data(output);

			SubscribeDataEventArgs^ args = gcnew SubscribeDataEventArgs(value);
			this->Received(this, args);
		}
	};

	void SubscribeDataWrapper::receive(hark::data output)
	{
		this->_outer->receive(output);
	}

	void
	NetworkDefWrapper::build(hark::Network& network, hark::DataSourceMap& input, hark::DataSinkMap& output)
	{
		Network^ _network = gcnew Network(&network);
		DataSourceMap^ _input = gcnew DataSourceMap(&input);
		DataSinkMap^ _output = gcnew DataSinkMap(&output);

		this->_def->build(_network, _input, _output);
	}

}
