#include "hark-lib/node.h"
#include "hark-lib/network.h"
#include "hark-lib/input.h"

#include "hark-lib/node/Constant.h"
#include "hark-lib/node/InputStream.h"
#include "hark-lib/node/MultiFFT.h"
#include "hark-lib/node/FeatureRemover.h"
#include "hark-lib/node/Delta.h"
#include "hark-lib/node/MelFilterBank.h"
#include "hark-lib/node/MSLSExtraction.h"
#include "hark-lib/node/PreEmphasis.h"
#include "hark-lib/node/SpeechRecognitionClient.h"
#include "hark-lib/node/SpectralMeanNormalizationIncremental.h"
#include "hark-lib/node/VADZC.h"
#include "hark-lib/node/SaveFeatures.h"
#include "hark-lib/node/SaveWavePCM.h"
#include "hark-lib/node/AudioStreamFromWave.h"

#include <stdlib.h>
#include <exception>
#include <iostream>
#include <fstream>

using namespace hark;

class HARK_Extracting: public hark::NetworkDef
{
public:
    void
    build(hark::Network &network, hark::DataSourceMap &input, hark::DataSinkMap &output)
    {
        auto &node_multi_fft = network.create<hark::Node, hark::node::MultiFFT>();
        auto &node_feature_remover = network.create<hark::Node, hark::node::FeatureRemover>();
        auto &node_delta = network.create<hark::Node, hark::node::Delta>();
        auto &node_mel_filter_bank = network.create<hark::Node, hark::node::MelFilterBank>();
        auto &node_msls_extraction = network.create<hark::Node, hark::node::MSLSExtraction>();
        auto &node_pre_emphasis = network.create<hark::Node, hark::node::PreEmphasis>();

        node_multi_fft
            << hark::Input("INPUT", input["CHANNEL_WAV"]);
            // << hark::Input("LENGTH", 512)
            // << hark::Input("WINDOW", "CONJ")
            // << hark::Input("WINDOW_LENGTH", 512);

        node_feature_remover
            << hark::Input("INPUT", node_delta["OUTPUT"])
            << hark::Input("SELECTOR", "40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81");

        node_mel_filter_bank
            << hark::Input("INPUT", node_pre_emphasis["OUTPUT"])
            // << hark::Input("LENGTH", 512)
            // << hark::Input("SAMPLING_RATE", 16000)
            // << hark::Input("CUTOFF", 8000)
            // << hark::Input("MIN_FREQUENCY", 63)
            // << hark::Input("MAX_FREQUENCY", 8000)
            << hark::Input("FBANK_COUNT", 40);

        node_msls_extraction
            << hark::Input("FBANK", node_mel_filter_bank["OUTPUT"])
            << hark::Input("SPECTRUM", node_pre_emphasis["OUTPUT"])
            << hark::Input("FBANK_COUNT", 40)
            << hark::Input("NORMALIZATION_MODE", "SPECTRAL")
            // << hark::Input("USE_LEGACY_MODE", true)
            // << hark::Input("USE_HTK_LIFTER", false)
            // << hark::Input("LIFTERING_COEF", 22)
            << hark::Input("USE_POWER", true);

        node_pre_emphasis
            << hark::Input("INPUT", node_multi_fft["OUTPUT"])
            // << hark::Input("LENGTH", 512)
            // << hark::Input("SAMPLING_RATE", 16000)
            // << hark::Input("PREEMCOEF", 0.97f)
            << hark::Input("INPUT_TYPE", "SPECTRUM");

        node_delta
            << hark::Input("INPUT", node_msls_extraction["OUTPUT"]);
            // << hark::Input("FBANK_COUNT", 41);

        output["OUTPUT"] << node_feature_remover["OUTPUT"];
    }
};

class HARK_Recording: public hark::NetworkDef
{
public:
    void
    build(hark::Network &network, hark::DataSourceMap &input, hark::DataSinkMap &output)
    {
        //auto &node_extracting = network.create<hark::Network, HARK_Extracting>("Extracting");

        auto &node_extracting = network.create_network(new HARK_Extracting(), new hark::dispatch::TriggeredRepeat(), "Extracting");

        auto &node_speech_recognition_client = network.create<hark::Node, hark::node::SpeechRecognitionClient>();
        auto &node_spectral_mean_normalization_incremental = network.create<hark::Node, hark::node::SpectralMeanNormalizationIncremental>();
        auto &node_vad_zc = network.create<hark::Node, hark::node::VADZC>();
        auto &node_save_features = network.create<hark::Node, hark::node::SaveFeatures>();
        auto &node_save_wave_PCM = network.create<hark::Node, hark::node::SaveWavePCM>();
        auto &node_audio_stream_from_wave = network.create<hark::Node, hark::node::AudioStreamFromWave, hark::dispatch::TriggeredRepeat>();

        node_audio_stream_from_wave
            << hark::LoopCondition(node_audio_stream_from_wave["NOT_EOF"])
            << hark::Input("INPUT", input["INPUT"]);
            // << hark::Input("LENGTH", 512)
            // << hark::Input("ADVANCE", 160)
            // << hark::Input("USE_WAIT", true);

        node_vad_zc
            << hark::Input("INPUT", node_audio_stream_from_wave["AUDIO"])
            // << hark::Input("ADVANCE", 160)
            // << hark::Input("LENGTH", 512)
            << hark::Input("CHANNEL_COUNT", 2)
            // << hark::Input("SAMPLING_RATE", 16000)
            // << hark::Input("STRIP_ZERO", true)
            // << hark::Input("ZERO_MEAN", false)
            << hark::Input("LEVEL_THRESHOLD", 60.f)
            << hark::Input("ZEROCROSS_THRESHOLD", 80)
            << hark::Input("HEAD_MARGIN", 400.f)
            << hark::Input("TAIL_MARGIN", 600.f);

        node_extracting
            << hark::Input("CHANNEL_WAV", node_vad_zc["OUTPUT"]);
        // << hark::Input("LENGTH", 512)
        // << hark::Input("SAMPLING_RATE", 16000);

        node_spectral_mean_normalization_incremental
            << hark::Input("INPUT", node_extracting["OUTPUT"])
            << hark::Input("NOT_EOF", true) // ToDo
            << hark::Input("SM_HISTORY", false) // ToDo
            // << hark::Input("FBANK_COUNT", 13)
            << hark::Input("PERIOD", 1)
            // << hark::Input("SM_ALGORITHM", "INCREMENTAL")
            // << hark::Input("SM_FILENAME", "")
            // << hark::Input("SM_HISTORY_FILENAME", "")
            // << hark::Input("IGNORE_FRAMES", false)
            << hark::Input("BASENAME", "")
            << hark::Input("OUTPUT_FNAME", "");
            // << hark::Input("SM_EXPORT_FILENAME", "")
            // << hark::Input("SM_EXPORT_ALGORITHM", "LAST_SRC");

        node_speech_recognition_client
            << hark::Input("FEATURES", node_spectral_mean_normalization_incremental["OUTPUT"])
            << hark::Input("MASKS", node_spectral_mean_normalization_incremental["OUTPUT"])
            << hark::Input("SOURCES", node_vad_zc["SOURCES"])
            << hark::Input("MFM_ENABLED", false);
            // << hark::Input("HOST", "127.0.0.1")
            // << hark::Input("PORT", 5530)
            // << hark::Input("SOCKET_ENABLED", true);

        node_save_features
            << hark::Input("FEATURES", node_spectral_mean_normalization_incremental["OUTPUT"])
            << hark::Input("SOURCES", node_vad_zc["SOURCES"])
            << hark::Input("BASENAME", "feature_");

        node_save_wave_PCM
            << hark::Input("ENABLE", false)
            << hark::Input("INPUT", node_vad_zc["OUTPUT"])
            << hark::Input("BASENAME", "pcm_{srcid}_");
            // << hark::Input("ADVANCE", 160)
            // << hark::Input("SAMPLING_RATE", 16000)
            // << hark::Input("BITS", "int16")
            // << hark::Input("INPUT_BITS", "as_BITS");
    }
};

class HARK_Main: public hark::NetworkDef
{
public:
    void
    build(hark::Network &network, hark::DataSourceMap &input, hark::DataSinkMap &output){

        auto &filename = network.create<hark::Node, hark::node::Constant, hark::dispatch::OneShot>()
                         << hark::Input("VALUE", "input.wav");

        auto &stream = network.create<hark::Node, hark::node::InputStream>()
                       << hark::Input("INPUT", filename["OUTPUT"]);

        //auto &loop = network.create<hark::Network, HARK_Recording, hark::dispatch::TriggeredRepeat>("HARK_Recording")
        //             << hark::Input("INPUT", stream["OUTPUT"]);

        auto &loop = network.create_network(new HARK_Recording(), new hark::dispatch::TriggeredRepeat(), "HARK_Recording")
                     << hark::Input("INPUT", stream["OUTPUT"]);
    }

};

int
process()
{
    // hark:data data(123.45);
    // std::cout << data.as<int>() << std::endl;
    // std::cout << data.as<float>() << std::endl;
    //
    std::shared_ptr<Network>network(hark::Network::create<HARK_Main>("HARK_Main1"));
    network->execute();

    return 0;
}

int
main(int argc, char *argv[])
{
    try{
        process();
    }catch(std::exception &ex){
        std::cerr << "Error in process: " << ex.what() << std::endl;
        return EXIT_FAILURE;
    }catch(...){
        std::cerr << "Error in process: " << "not std::exception type" << std::endl;
        throw;
    }
    return EXIT_SUCCESS;
}
