#! /usr/bin/env python
# -*- coding: utf-8 -*-

import harklib
from hark import NodeDef

import types
import sys

_nodes = (
    # hark-base nodes
    'AND',
    'Constant',
    'Delay',
    'Equal',
    'Greater',
    'GreaterEqual',
    'IF',
    'InputStream',
    'IterCount',
    'Iterate',
    'NOP',
    'NOT',
    'OR',
    #'Sleep',
    'Smaller',
    'SmallerEqual',
    #'',
    # hark-core nodes
    'AudioStreamFromMemory',
    'AudioStreamFromWave',
    #'BF',
    'BGNEstimator',
    'CMChannelSelector',
    'CMConjEachElement',
    'CMDivideEachElement',
    'CMIdentityMatrix',
    'CMInverseMatrix',
    'CMLoad',
    'CMMakerFromFFT',
    'CMMakerFromFFTwithFlag',
    'CMMultiplyEachElement',
    'CMMultiplyMatrix',
    'CMSave',
    #'CSP',
    'CalcSpecAddPower',
    'CalcSpecSubGain',
    'ChannelSelector',
    'CombineMap',
    'CombineSource',
    'ConstantLocalization',
    #'DataLogger',
    'Delta',
    #'DeltaMask',
    #'DeltaPowerMask',
    #'DisplayLocalization',
    'EstimateLeak',
    #'EstimateTF',
    'FeatureRemover',
    'GHDSS',
    #'HRLE',
    #'HarkDataStreamSender',
    #'HarkParamDynReconf',
    #'LoadMapFrames',
    #'LoadSourceFrames',
    #'LoadVectorFrames',
    #'LocalizeBFDS',
    'LocalizeMUSIC',
    #'MFCCExtraction',
    #'MFMGeneration',
    #'ML',
    'MSLSExtraction',
    #'MSNR',
    #'MVDR',
    #'MapIDOffset',
    #'MapMatrixValueOverwrite',
    #'MapOperator',
    #'MapSelectorBySource',
    #'MapToMap',
    #'MapToMatrix',
    #'MapToVector',
    #'MapVectorValueOverwrite',
    #'MatrixToMap',
    #'MatrixToMatrix',
    #'MatrixToVector',
    #'MatrixValueOverwrite',
    'MelFilterBank',
    #'MultiDownSampler',
    'MultiFFT',
    'MultiGain',
    #'MultiLocalizationIntegrator',
    #'NormalizeMUSIC',
    #'PlayAudio',
    #'PostFilter',
    'PowerCalcForMap',
    'PowerCalcForMatrix',
    'PreEmphasis',
    #'ResizeMapMatrixValue',
    #'ResizeMapVectorValue',
    'SaveFeatures',
    'SaveHTKFeatures',
    #'SaveMapFrames',
    #'SaveMatrixFrames',
    #'SaveRawPCM',
    #'SaveSourceLocation',
    #'SaveVectorFrames',
    'SaveWavePCM',
    #'SegmentAudioStreamByID',
    'SemiBlindICA',
    'SourceIntervalExtender',
    #'SourceSelectorByDirection',
    #'SourceSelectorByID',
    #'SourceSelectorBySourceInfo',
    'SourceTracker',
    #'SourceTrackerPF',
    #'SourceTransformer',
    'SpectralGainFilter',
    #'SpectralMeanNormalization',
    'SpectralMeanNormalizationIncremental',
    'SpeechRecognitionClient',
    #'SpeechRecognitionSMNClient',
    #'SubscriberFLAC',
    'Synthesize',
    #'TextConcatenate',
    #'TextConverter',
    ##'VADPyAnnote',
    'VADZC',
    #'VectorToMap',
    #'VectorToMatrix',
    #'VectorToVector',
    #'VectorValueOverwrite',
    'WhiteNoiseAdder',
    #'',
    # 'PublishData',
    # 'SubscribeData',
    #'PlotQuickSourceKivy',
    'Print'
)

def define_nodeclass(n):
    hcls = getattr(harklib, n)

    cls = types.new_class(n, (NodeDef,), exec_body=lambda d: d.update({
        '__module__': __name__,
    }))

    init = lambda self, **kwargs: super(cls, self).__init__(hcls())
    init.__name__ = n

    def call(self, **kwargs):
        return self._inner(**kwargs)

    cls.__init__ = init
    cls.__call__ = call
    globals()[n] = cls

for n in _nodes:
    define_nodeclass(n)

class PublishData(NodeDef):
    def __init__(self):
        self._inner = harklib.PublishData()
        super().__init__(self._inner)

    def close(self):
        self._inner.close()

    def push(self, data):
        self._inner.push(data)

class SubscribeDataWrapper(harklib.SubscribeData):
    def __init__(self, ndef: 'SubscribeData'):
        # print('SubscribeDataWrapper.__init__')
        super().__init__()
        self._def = ndef

    def receive(self, data):
        self._def.receive(data)

class SubscribeData(NodeDef):
    def __init__(self):
        # print('SubscribeData.__init__')
        super().__init__(SubscribeDataWrapper(self))

    def receive(self, data):
        raise NotImplementedError("User must override method SubscribeData.receive()")

class PythonNodeWrapper(harklib.PythonNode):
    def __init__(self, ndef: 'PythonNode', inputs, outputs):
        super().__init__(inputs, outputs)
        self._def = ndef

    # def __del__(self):
    #     if hasattr(super(), '__del__'):
    #         super().__del__()
    #     print('PythonNodeWrapper.__del__()')

    def call(self, kwargs):
        return self._def.forward(**kwargs)

class PythonNode(NodeDef):
    def __init__(self, inputs, outputs):
        if type(inputs) is not list:
            inputs = [inputs]
        if type(outputs) is not list:
            outputs = [outputs]
        super().__init__(PythonNodeWrapper(self, inputs, outputs))

    # def __del__(self):
    #     if hasattr(super(), '__del__'):
    #         super().__del__()
    #     print('PythonNode.__del__()')

    def __call__(self, **kwargs):
        return self._inner(**kwargs)

    def forward(self, **kwargs):
        raise NotImplementedError("User must override method PythonNode.forward()")

# end of file
