//
//  Network.m
//  HARKlib
//
//  Created by Tomoyoshi Kinoshita on 2021/04/01.
//

#import <Foundation/Foundation.h>
#include "Network.h"

#include "hark-lib/data.h"
#include "hark-lib/network.h"
#include "hark-lib/dispatch.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/CMMultiplyEachEelement.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/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/VADPyAnnote.h"
#include "hark-lib/node/PublishData.h"
#include "hark-lib/node/SubscribeData.h"

@implementation Source

- (nonnull id)init {
    if(self = [super init]){
        self->_id = 0;
        self->_mic_id = 0;
        self->_sub_id = 0;
        self->_power = 0;
        self->_x = 0;
        self->_y = 0;
        self->_z = 0;
    }
    return self;
}

- (nonnull id)initWithId: (int)id x:(float)x y:(float)y z:(float)z subId: (int)subId micId: (int)micId power: (float)power {
    if(self = [super init]){
        self->_id = id;
        self->_mic_id = micId;
        self->_sub_id = subId;
        self->_power = power;
        self->_x = x;
        self->_y = y;
        self->_z = z;
    }
    return self;
}

- (nonnull NSArray *)get_position {
    NSNumber *x = [NSNumber numberWithFloat: self->_x];
    NSNumber *y = [NSNumber numberWithFloat: self->_y];
    NSNumber *z = [NSNumber numberWithFloat: self->_z];
    return @[x, y, z];
}

- (nonnull NSString *)description {
    return [NSString stringWithFormat: @"Source: position=(%f, %f, %f), power=%f, id=%d, sub_id=%d, mic_id=%d", self->_x, self->_y, self->_z, self->_power, self->_id, self->_sub_id, self->_mic_id];
}

@end

#define BEGIN_IMPLEMENT_NODEDEF(type) \
    @implementation type \

#define END_IMPLEMENT_NODEDEF(type) \
    - (id)init \
    { \
        if(self = [super init: new hark::node::type]){ \
        } \
        return self; \
    } \
    - (void)build: (Network *)network \
            input: (DataSourceMap *)input \
            output: (DataSinkMap *)output{ \
    } \
    @end \

#define IMPLEMENT_NODEDEF(type) \
    BEGIN_IMPLEMENT_NODEDEF(type) \
    END_IMPLEMENT_NODEDEF(type) \

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 = nil;
        this->_def = nil;
    }
    
    N *get_n() const { return this->_n; }
    D *get_def() const { return this->_def; }
    
private:
    N *_n;
    D *_def;
};

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

@implementation BaseDispatcher {
    hark::dispatch::BaseDispatcher *_dispatcher;
}

- (id)init: (hark::dispatch::BaseDispatcher *)dispatcher
{
    if(self = [super init]){
        self->_dispatcher = dispatcher;
    }
    return self;
}

- (hark::dispatch::BaseDispatcher *)inner {
    return self->_dispatcher;
}

@end

//template<typename X> class HOGE {
//
//};

//class WrapperNode: public hark::BaseNode
//{
//public:
//    WrapperNode(hark::BaseNode *innerNode):
//        hark::BaseNode(), _innerNode(innerNode)
//    {
//    }
//
//    virtual ~WrapperNode()
//    {
//    }
//
//    virtual hark::DataSource &operator [] (const std::string &name);
//    virtual void initialize();
//    virtual void dump_graph(std::ostream &os, bool with_headers = false) const;
//    virtual bool try_process();
//
//private:
//    hark::BaseNode *_innerNode;
//
//};

@implementation DataSink {
    hark::DataSink *_dataSink;
}

- (id)init: (hark::DataSink *)dataSink
{
    if(self = [super init]){
        self->_dataSink = dataSink;
    }
    return self;
}

- (hark::DataSink *)inner {
    return self->_dataSink;
}

@end

@implementation DataSource {
    hark::DataSource *_dataSource;
}

- (id)init: (hark::DataSource *)dataSource
{
    if(self = [super init]){
        self->_dataSource = dataSource;
    }
    return self;
}

- (hark::DataSource *)inner {
    return self->_dataSource;
}

@end

@implementation DataSinkMap {
    hark::DataSinkMap *_dataSinkMap;
}

- (id)init: (hark::DataSinkMap *)dataSinkMap
{
    if(self = [super init]){
        self->_dataSinkMap = dataSinkMap;
    }
    return self;
}

- (hark::DataSinkMap *)inner {
    return self->_dataSinkMap;
}

- (nonnull DataSinkMap *)add_input: (nonnull NSString *)name
                            source: (nonnull DataSource *)source
{
    self->_dataSinkMap->operator[] ([name UTF8String]) << *[source inner];
    return self;
}

@end

@implementation DataSourceMap {
    hark::DataSourceMap *_dataSourceMap;
}

- (id)init: (hark::DataSourceMap *)dataSourceMap
{
    if(self = [super init]){
        self->_dataSourceMap = dataSourceMap;
    }
    return self;
}

- (hark::DataSourceMap *)inner {
    return self->_dataSourceMap;
}

- (DataSource *)get: (nonnull NSString *)name
{
    hark::DataSource &ds = self->_dataSourceMap->get([name UTF8String]);
    return [[DataSource alloc] init: &ds];
}

@end

@implementation NodeDef {
    hark::NodeDef *_nodedef;
}

- (id)init: (hark::NodeDef *)nodedef
{
    if(self = [super init]){
        self->_nodedef = nodedef;
    }
    return self;
}

- (hark::NodeDef *)inner {
    return self->_nodedef;
}

@end

@class NetworkDef;

class NetworkDefWrapper: public hark::NetworkDef
{
    ::NetworkDef *_def;
    
public:
    NetworkDefWrapper(::NetworkDef *def):
        _def(def)
    {
    }

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

@implementation NetworkDef {
    hark::NetworkDef *_networkdef;
}

- (id)init
{
    if(self = [super init]){
        self->_networkdef = new NetworkDefWrapper(self);
    }
    return self;
}

- (void)build: (Network *)network
        input: (DataSourceMap *)input
        output: (DataSinkMap *)output
{

}

- (hark::NetworkDef *)inner
{
    return self->_networkdef;
}

@end

@implementation BaseNode {
    hark::BaseNode *_node;
}

- (id)init: (hark::BaseNode *)node
{
    if(self = [super init]){
        self->_node = node;
    }
    return self;
}

- (nonnull BaseNode *)add_input: (nonnull NSString *)name
                         source: (nonnull DataSource *)source
{
    self->_node->add_input(hark::Input([name UTF8String], *[source inner]));
    return self;
}

- (nonnull BaseNode *)add_input: (nonnull NSString *)name
                         string: (nonnull NSString *)string
{
    self->_node->add_input(hark::Input([name UTF8String], std::string([string UTF8String])));
    return self;
}

- (nonnull BaseNode *)add_input: (nonnull NSString *)name
                         number: (nonnull NSNumber *)number
{
    self->_node->add_input(hark::Input([name UTF8String], [number floatValue]));
    return self;
}

- (nonnull BaseNode *)add_input: (nonnull NSString *)name
                        boolean: (bool)value
{
    self->_node->add_input(hark::Input([name UTF8String], value));
    return self;
}

- (nonnull BaseNode *)loop_condition: (nonnull DataSource *)source
{
    self->_node->loop_condition(hark::LoopCondition(*[source inner]));
    return self;
}

- (nonnull DataSource *)get: (nonnull NSString *)name
{
    hark::DataSource &source = self->_node->get([name UTF8String]);
    return [[DataSource alloc] init: &source];
}

@end

@implementation Network {
    hark::Network *_network;
}

//+ (void)load {
//    hark::data a;
//  auto ary = [@"a,b" componentsSeparatedByString:@","];
//  if (auto last = ary.lastObject) {
//    puts(last.UTF8String); // "b"
//  }
//  [@"123" substringWithRange:{1, 2}]; // @"23"
//}

- (id)init: (hark::Network *)network
{
    if(self = [super init]){
        self->_network = network;
    }
    return self;
}

- (void)dealloc
{
////    delete self->_network;
//    std::cout << "Network::dealloc" << std::endl;
}

- (nonnull BaseNode *)create: (nonnull NodeDef *)def
                        name: (nonnull NSString *)name
                  dispatcher: (nonnull BaseDispatcher *)dispatcher
{
    hark::BaseNode &node = self->_network->create_node([def inner], [dispatcher inner], [name UTF8String]);
    BaseNode *bn = [[BaseNode alloc] init: &node];
    node.attach_userdata(new NodeUserData(bn, def));
    return bn;
}

- (nonnull BaseNode *)createNode: (nonnull NodeDef *)def
                            name: (nonnull NSString *)name
                      dispatcher: (nonnull BaseDispatcher *)dispatcher
{
    hark::BaseNode &node = self->_network->create_node([def inner], [dispatcher inner], [name UTF8String]);
    BaseNode *bn = [[BaseNode alloc] init: &node];
    node.attach_userdata(new NodeUserData(bn, def));
    return bn;
}

- (nonnull BaseNode *)createNode: (nonnull NodeDef *)def
                            name: (nonnull NSString *)name
{
    return [self createNode:def name:name dispatcher:[[TriggeredOneShotDispatcher alloc] init]];
}

- (nonnull BaseNode *)createNode: (nonnull NodeDef *)def
                      dispatcher: (nonnull BaseDispatcher *)dispatcher
{
    return [self createNode:def name:@"_name_" dispatcher:dispatcher];
}

- (nonnull BaseNode *)createNode: (nonnull NodeDef *)def
{
    return [self createNode:def name:@"_name_" dispatcher:[[TriggeredOneShotDispatcher alloc] init]];
}

- (nonnull Network *)createNetwork: (nonnull NetworkDef *)def
                              name: (nonnull NSString *)name
                        dispatcher: (nonnull BaseDispatcher *)dispatcher
{
    hark::Network &network = self->_network->create_network([def inner], [dispatcher inner], [name UTF8String]);
    Network *n = [[Network alloc] init: &network];
    network.attach_userdata(new NetworkUserData(n, def));
    return n;
}


- (nonnull Network *)createNetwork: (nonnull NetworkDef *)def
                              name: (nonnull NSString *)name
{
    return [self createNetwork:def name:name dispatcher:[[TriggeredOneShotDispatcher alloc] init]];
}

- (nonnull Network *)createNetwork: (nonnull NetworkDef *)def
                        dispatcher: (nonnull BaseDispatcher *)dispatcher
{
    return [self createNetwork:def name:@"_name_" dispatcher:dispatcher];
}

- (nonnull Network *)createNetwork: (nonnull NetworkDef *)def
{
    return [self createNetwork:def name:@"_name_" dispatcher:[[TriggeredOneShotDispatcher alloc] init]];
}

+ (nonnull Network *)createNetwork: (nonnull NetworkDef *)def
                              name: (nonnull NSString *)name
{
    hark::Network *network = new hark::Network([def inner], [name UTF8String]);
    Network *n = [[Network alloc] init: network];
    network->attach_userdata(new NetworkUserData(n, def));
    return n;
}

//- (nonnull BaseNode *)create: (nonnull id)type
//                  dispatcher: (nonnull id)dispatcher
//{
//    return [self create: type, name: "Network", dispatcher: dispatcher];
//}
//
//- (nonnull BaseNode *)create: (nonnull id)type
//                        name: (nonnull NSString *)name;
//- (nonnull BaseNode *)create: (nonnull id)type;

- (nonnull Network *)add_input: (nonnull NSString *)name
                        source: (nonnull DataSource *)source
{
    self->_network->add_input(hark::Input([name UTF8String], *[source inner]));
    return self;
}

- (nonnull Network *)add_input: (nonnull NSString *)name
                        string: (nonnull NSString *)string
{
    self->_network->add_input(hark::Input([name UTF8String], std::string([string UTF8String])));
    return self;
}

- (nonnull Network *)add_input: (nonnull NSString *)name
                        number: (nonnull NSNumber *)number
{
    self->_network->add_input(hark::Input([name UTF8String], [number floatValue]));
    return self;
}

- (nonnull Network *)add_input: (nonnull NSString *)name
                       boolean: (bool)value
{
    self->_network->add_input(hark::Input([name UTF8String], value));
    return self;
}

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

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

- (nonnull DataSource *)get: (nonnull NSString *)name
{
    hark::DataSource &source = self->_network->get([name UTF8String]);
    return [[DataSource alloc] init: &source];
}

- (nullable NodeDef *)query_nodedef: (nonnull NSString *)name
{
    hark::BaseNode *node = self->_network->query_node([name UTF8String]);
    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 nil;
}

@end

@implementation OneShotDispatcher
- (id)init
{
    if(self = [super init: new hark::dispatch::OneShot()]){
    }
    return self;
}

@end

@implementation RepeatDispatcher
- (id)init
{
    if(self = [super init: new hark::dispatch::Repeat()]){
    }
    return self;
}
@end

@implementation TriggeredOneShotDispatcher
- (id)init
{
    if(self = [super init: new hark::dispatch::TriggeredOneShot()]){
    }
    return self;
}
@end

@implementation TriggeredMultiShotDispatcher
- (id)init
{
    if(self = [super init: new hark::dispatch::TriggeredMultiShot()]){
    }
    return self;
}
@end

@implementation TriggeredRepeatDispatcher
- (id)init
{
    if(self = [super init: new hark::dispatch::TriggeredRepeat()]){
    }
    return self;
}
@end

void
NetworkDefWrapper::build(hark::Network &network, hark::DataSourceMap &input, hark::DataSinkMap &output)
{
    Network *_network = [[Network alloc] init: &network];
    DataSourceMap *_input = [[DataSourceMap alloc] init: &input];
    DataSinkMap *_output = [[DataSinkMap alloc] init: &output];
    [this->_def build:_network input:_input output:_output];
}


IMPLEMENT_NODEDEF(AND)
IMPLEMENT_NODEDEF(Constant)
IMPLEMENT_NODEDEF(Delay)
IMPLEMENT_NODEDEF(Equal)
IMPLEMENT_NODEDEF(Greater)
IMPLEMENT_NODEDEF(GreaterEqual)
IMPLEMENT_NODEDEF(IF)
IMPLEMENT_NODEDEF(InputStream)
IMPLEMENT_NODEDEF(IterCount)
IMPLEMENT_NODEDEF(Iterate)
IMPLEMENT_NODEDEF(NOP)
IMPLEMENT_NODEDEF(NOT)
IMPLEMENT_NODEDEF(OR)
//IMPLEMENT_NODEDEF(Sleep)
IMPLEMENT_NODEDEF(Smaller)

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

IMPLEMENT_NODEDEF(Print)

BEGIN_IMPLEMENT_NODEDEF(PublishData)

- (void)push: (nonnull NSObject *)value
{
    
}

- (void)pushInt16Vector:(const SInt16 *)value
                  count:(size_t)count
{
    std::vector<short int> vector_value(value, value + count);
    hark::data data_value(vector_value);
    
    hark::node::PublishData *publish_data = dynamic_cast<hark::node::PublishData *>([self inner]);
    publish_data->push(data_value);
}

END_IMPLEMENT_NODEDEF(PublishData)

class SubscribeDataWrapper: public hark::node::SubscribeData
{
public:
    SubscribeDataWrapper():
        _delegate(nil)
    {
    }

    SubscribeDataWrapper(id<SubscribeDataDelegate> delegate):
        _delegate(delegate)
    {
    }
    
    NSObject *object_from_data(hark::data value)
    {
        if(value.is_type<std::map<int, hark::data>>()){
            NSMutableDictionary *output = [[NSMutableDictionary alloc] init];

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

            return output;

        }else if(value.is_type<std::vector<hark::data>>()){
            NSMutableArray *output = [[NSMutableArray array] init];
            
            const auto &vec = value.cast<std::vector<hark::data>>();
            for(size_t i = 0; i < vec.size(); i++){
                [output addObject:this->object_from_data(vec[i])];
            }
            
            return output;

        }else if(value.is_type<std::vector<float>>()){
            NSMutableArray *output = [[NSMutableArray array] init];
            
            const auto &vec = value.cast<std::vector<float>>();
            for(size_t i = 0; i < vec.size(); i++){
                [output addObject:[NSNumber numberWithFloat:vec[i]]];
            }
            
            return output;

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

            Source *output = [[Source alloc] initWithId:src.id x:src.x[0] y:src.x[1] z:src.x[2] subId:src.sub_id micId:src.mic_id power:src.power];
            return output;

        }else{
            return nil;
        }
    }

    virtual void receive(hark::data output)
    {
        NSObject *value = this->object_from_data(output);
        if(this->_delegate){
            [this->_delegate receive:value];
        }
    }
    
    void set_delegate(id<SubscribeDataDelegate> delegate)
    {
        this->_delegate = delegate;
    }
    
    void remove_delegate()
    {
        this->_delegate = nil;
    }

private:
    id<SubscribeDataDelegate> _delegate; // SubscribeDataDelegate

};

BEGIN_IMPLEMENT_NODEDEF(SubscribeData)

- (id)init
{
    hark::node::SubscribeData *sd = new SubscribeDataWrapper();
    if(self = [super init: sd]){
    }
    return self;
}

- (id)init: (id<SubscribeDataDelegate>)delegate
{
    auto sd = new SubscribeDataWrapper(delegate);
    if(self = [super init: sd]){
    }
    return self;
}

- (void)set_delegate: (nonnull id<SubscribeDataDelegate>)delegate
{
    hark::NodeDef *nodedef = [self inner];
    SubscribeDataWrapper *sd = dynamic_cast<SubscribeDataWrapper *>(nodedef);
    if(sd){
        sd->set_delegate(delegate);
    }
}

- (void)build: (Network *)network
        input: (DataSourceMap *)input
        output: (DataSinkMap *)output{
}

@end

