/*
   HARK Library
   Copyright (C) 2021 Honda Research Institute Japan Inc.
 */

#include "hark-lib/node/SpeechRecognitionClient.h"

namespace hark
{
namespace node
{
    SpeechRecognitionClient::SpeechRecognitionClient():
        NodeDef("SpeechRecognitionClient")
    {
        *this
            << Terminal::in("FEATURES")
            << Terminal::in("MASKS")
            << Terminal::in("SOURCES")
            << Terminal::in("MFM_ENABLED").def(false)
            << Terminal::in("HOST").def("127.0.0.1")
            << Terminal::in("PORT").def(5530)
            << Terminal::in("SOCKET_ENABLED").def(true)
            << Terminal::in("PROTOCOL_TYPE").def(1)
            >> Terminal::out("OUTPUT");
            std::random_device rnd;
            std::mt19937_64 mt(rnd());
            this->client_id = mt();
    }

    SpeechRecognitionClient::~SpeechRecognitionClient()
    {
        for(std::map<int, MfcnetClient*>::iterator it = this->socket_map.begin(); it != this->socket_map.end();){
            timeval tv;
            ::gettimeofday(&tv, NULL);
            std::cout << "SpeechRecognitionClient: end " << "destructor" << " " << it->first << " " << tv.tv_sec << "." << tv.tv_usec << "\n";
            //this->socket_map[it->first]->disconnect();
            //delete this->socket_map[it->first];
            it->second->disconnect();
            delete it->second;
            this->socket_map.erase(it++);
        }
    }

    void
    SpeechRecognitionClient::reset()
    {
    }

    void
    SpeechRecognitionClient::initialize(InputDataBundle &input)
    {
        this->mfm_enabled = input["MFM_ENABLED"].last().as<bool>();
        this->socket_enabled = input["SOCKET_ENABLED"].last().as<bool>();
        this->hostname = input["HOST"].last().cast<std::string>();
        this->port = static_cast<unsigned short int>(input["PORT"].last().cast<int>());
        this->protocol_type = input["PROTOCOL_TYPE"].last().as<int>();
    }

    bool
    SpeechRecognitionClient::operator () (size_t count, InputDataBundle &input, OutputDataBundle &output)
    {
        if(count == 0){ this->initialize(input); }
        const auto &features = input["FEATURES"].last().cast<std::map<int, data> >();
        const auto &masks = (this->mfm_enabled ?
                             input["MASKS"].last().cast<std::map<int, data> >() : features);
        const auto &sources = input["SOURCES"].last().cast<std::vector<data> >();

        for(std::vector<data>::const_iterator it = sources.begin(); it != sources.end(); ++it){
            const hark::Source &src = it->cast<hark::Source>();
            std::map<int, MfcnetClient*>::iterator it_sock = this->socket_map.find(src.id);
            if(it_sock == this->socket_map.end()){
                if(this->protocol_type == 1){
                    hark::SourceInfo source_info;
                    timeval tv;
                    source_info.source_id = src.id;
                    source_info.azimuth = 180 / M_PI * std::atan2(src.x[1], src.x[0]);
                    source_info.elevation = 180 / M_PI * std::asin(src.x[2]);
                    ::gettimeofday(&tv, NULL);
                    source_info.tv_sec = tv.tv_sec;
                    source_info.tv_usec = tv.tv_usec;

                    this->socket_map.emplace(src.id, new MfcnetClient(this->hostname, this->port, !this->socket_enabled));
                    this->socket_map[src.id]->write(source_info);
                    std::cout << "source id " << src.id << " is begin recognized.\n";
                }
                else if(this->protocol_type == 2){
                    hark::UtteranceInfo utterance_info;
                    timeval tv;
                    utterance_info.client_id = this->client_id;
                    utterance_info.mic_index = src.mic_id;
                    utterance_info.source_id = src.id;
                    utterance_info.sub_id = src.sub_id;
                    utterance_info.azimuth = 180 / M_PI * std::atan2(src.x[1], src.x[0]);
                    utterance_info.elevation = 180 / M_PI * std::asin(src.x[2]);
                    ::gettimeofday(&tv, NULL);
                    utterance_info.tv_sec = tv.tv_sec;
                    utterance_info.tv_usec = tv.tv_usec;

                    this->socket_map.emplace(src.id, new MfcnetClient(this->hostname, this->port, !this->socket_enabled));
                    this->socket_map[src.id]->write(utterance_info);
                    std::cout << "source id " << src.id << " is begin recognized.\n";
                }
                else{
                    std::stringstream ss;
                    ss << "An unsupported protocol type was specified.\n";
                    throw RuntimeError(ss.str());
                }
            }
            std::map<int, data>::const_iterator it_feats = features.find(src.id);
            std::map<int, data>::const_iterator it_masks = masks.find(src.id);
            if(it_feats != features.end() && it_masks != masks.end()){
                std::vector<float> &feats_vec = it_feats->second.cast<std::vector<float> >();
                this->socket_map[src.id]->write(feats_vec);
                if(this->protocol_type == 1){
                    if(this->mfm_enabled){
                        std::vector<float> &masks_vec = it_masks->second.cast<std::vector<float> >();
                        if(feats_vec.size() != masks_vec.size()){
                            std::stringstream ss;
                            ss << "mask size mismatch. feats_vec.size()=" << feats_vec.size() << ", masks_vec.size()=" << masks_vec.size() << "\n";
                            throw RuntimeError(ss.str());
                        }
                        this->socket_map[src.id]->write(masks_vec);
                    }
                    else{
                        std::vector<float> masks_vec = std::vector<float>(feats_vec.size(), 1.0f);
                        this->socket_map[src.id]->write(masks_vec);
                    }
                }
            }
        }

        for(std::map<int, MfcnetClient*>::iterator it = this->socket_map.begin(); it != this->socket_map.end();){
            std::map<int, data>::const_iterator it_feats = features.find(it->first);
            std::map<int, data>::const_iterator it_masks = masks.find(it->first);
            bool is_terminated = false;
            if(it_feats == features.end()){
                std::cout << "source id " << it->first << " is features terminated.\n";
                is_terminated = true;
            }
            if(it_masks == masks.end()){
                std::cout << "source id " << it->first << " is masks terminated.\n";
                is_terminated = true;
            }
            if(is_terminated){
                timeval tv;
                ::gettimeofday(&tv, NULL);
                std::cout << "SpeechRecognitionClient: end " << count << " " << it->first << " " << tv.tv_sec << "." << tv.tv_usec << "\n";
                //this->socket_map[it->first]->disconnect();
                //delete this->socket_map[it->first];
                it->second->disconnect();
                delete it->second;
                this->socket_map.erase(it++);
            }
            else{
                ++it;
            }
        }

        output["OUTPUT"].push(input["FEATURES"].last());
        return true;
    }
}
}

/* end of file */
