#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/AudioStreamFromWave.h"
#include "hark-lib/node/ChannelSelector.h"
#include "hark-lib/node/SemiBlindICA.h"
#include "hark-lib/node/Synthesize.h"
#include "hark-lib/node/SaveWavePCM.h"

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

using namespace hark;

class Recording: public hark::NetworkDef
{
public:
    std::map<std::string, std::string> _args;
    bool use_vad_pyannote;
    std::string output_path;
    float onset_threshold, offset_threshold;
    std::string norm_mean, norm_sigma, norm_count;
public:
    Recording(const std::map<std::string, std::string> &args):
        _args(args),
        use_vad_pyannote(false),
        output_path("."),
        onset_threshold(0.1),
        offset_threshold(-0.2),
        norm_mean("0 0 0 0"),
        norm_sigma("0 0 0 0"),
        norm_count("0 0 0 0")
    {
        std::map<std::string, std::string>::const_iterator it;
        if((it = args.find("use-vad-pyannote")) != args.end()){
            std::stringstream ss(it->second);
            ss >> this->use_vad_pyannote;
            std::cout << "use-vad-pyannote: " << this->use_vad_pyannote << std::endl;
        }
        if((it = args.find("output-path")) != args.end()){
            this->output_path = it->second;
        }
        if((it = args.find("onset-threshold")) != args.end()){
            std::stringstream(it->second) >> this->onset_threshold;
        }
        if((it = args.find("offset-threshold")) != args.end()){
            std::stringstream(it->second) >> this->offset_threshold;
        }
        if((it = args.find("norm-mean")) != args.end()){
            this->norm_mean = it->second;
            std::cout << "norm-mean: " << this->norm_mean << std::endl;
        }
        if((it = args.find("norm-sigma")) != args.end()){
            this->norm_sigma = it->second;
            std::cout << "norm-sigma: " << this->norm_sigma << std::endl;
        }
        if((it = args.find("norm-count")) != args.end()){
            this->norm_count = it->second;
            std::cout << "norm-count: " << this->norm_count << std::endl;
        }
    }

public:
    void
    build(hark::Network &network, hark::DataSourceMap &input, hark::DataSinkMap &output)
    {
        auto &node_audio_stream_from_wave = network.create<hark::Node, hark::node::AudioStreamFromWave, hark::dispatch::TriggeredRepeat>();
        auto &node_channel_selector_1 = network.create<hark::Node, hark::node::ChannelSelector>();
        auto &node_channel_selector_2 = network.create<hark::Node, hark::node::ChannelSelector>();
        auto &node_multi_fft_1 = network.create<hark::Node, hark::node::MultiFFT>();
        auto &node_multi_fft_2 = network.create<hark::Node, hark::node::MultiFFT>();
        auto &node_semi_blind_ica = network.create<hark::Node, hark::node::SemiBlindICA>();
        auto &node_synthesize = network.create<hark::Node, hark::node::Synthesize>();
        auto &node_save_wave_pcm = network.create<hark::Node, hark::node::SaveWavePCM>();

        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_channel_selector_1
            << hark::Input("INPUT", node_audio_stream_from_wave["AUDIO"])
            << hark::Input("SELECTOR", "0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15");
        node_channel_selector_2
            << hark::Input("INPUT", node_audio_stream_from_wave["AUDIO"])
            << hark::Input("SELECTOR", "16");

        node_multi_fft_1
            << hark::Input("INPUT", node_channel_selector_1["OUTPUT"]);
        node_multi_fft_2
            << hark::Input("INPUT", node_channel_selector_2["OUTPUT"]);

        node_semi_blind_ica
            << hark::Input("INPUT", node_multi_fft_1["OUTPUT"])
            << hark::Input("REFERENCE", node_multi_fft_2["OUTPUT"])
            << hark::Input("CHANNEL", 16);

        node_synthesize
            << hark::Input("INPUT", node_semi_blind_ica["OUTPUT"]);
        
        node_save_wave_pcm
            << hark::Input("INPUT", node_synthesize["OUTPUT"]);
    }
};

class Main: public hark::NetworkDef
{
public:
    std::map<std::string, std::string> _args;
public:
    Main(const std::map<std::string, std::string> &args): _args(args)
    {
    }

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", "test_sb.wav");

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

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

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

};

int
process(int argc, char *argv[])
{
    std::map<std::string, std::string> args;
    for(int i = 1; i < argc; i++){
        // printf("%s\n", argv[i]);
        char *del = strchr(argv[i], '=');
        *del = '\0';
        std::string key = argv[i];
        std::string value = del + 1;
        // std::stringstream ss(argv[i]);
        // std::string key, value;
        // std::getline(ss, key, '=');
        // value = argv[i] + (key.size() + 1);
        // // std::getline(ss, value, '\0');
        // fprintf(stdout, "k=%s, v=%s\n", key.c_str(), value.c_str());
        args[key] = value;
    }
    // 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<Main>("Main", args));
    network->execute();

    return 0;
}

int
main(int argc, char *argv[])
{
    try{
        process(argc, argv);
    }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;
}
