
#include <cmath>
#include <complex>
#include <algorithm>
#include <iostream>

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

namespace hark
{
namespace node
{
    CombineSource::CombineSource():
        NodeDef("CombineSource")
    {
        *this
            << Terminal::in("SOURCE1")
            << Terminal::in("SOURCE2")
            << Terminal::in("DUPLICATE_ID_HANDLING_MODE").def("IGNORE")
            << Terminal::in("SOURCE1_OFFSET").def(0)
            << Terminal::in("SOURCE2_OFFSET").def(10000)
            >> Terminal::out("SRCOUT");
    }

    void
    CombineSource::reset()
    {
    }

    void
    CombineSource::initialize(InputDataBundle &input)
    {
        const auto &hm = input["DUPLICATE_ID_HANDLING_MODE"].last().cast<std::string>();
        if(hm == "IGNORE"){
            this->handling_mode = HANDLING_MODE::IGNORE;
        }
        else if(hm == "PRIORITIZE_SOURCE1"){
            this->handling_mode = HANDLING_MODE::PRIORITIZE_SOURCE1;
        }
        else if(hm == "PRIORITIZE_SOURCE2"){
            this->handling_mode = HANDLING_MODE::PRIORITIZE_SOURCE2;
        }
        else if(hm == "USE_OFFSET"){
            this->handling_mode = HANDLING_MODE::USE_OFFSET;
            this->source1_offset = input["SOURCE1_OFFSET"].last().as<int>();
            this->source2_offset = input["SOURCE2_OFFSET"].last().as<int>();
        }
        else{
            std::stringstream ss;
            ss << "CombineSource: Unexpected DUPLICATE_ID_HANDLING_MODE value '" << hm << "', expected 'IGNORE', 'PRIORITIZE_SOURCE1', 'PRIORITIZE_SOURCE2' or 'USE_OFFSET'.\n" << "FILE: " << __FILE__ << ", LINE: " << __LINE__;
            throw RuntimeError(ss.str());
        }
    }

    bool
    CombineSource::operator () (size_t count, InputDataBundle &input, OutputDataBundle &output)
    {
        // No rewriting occurs, but don't qualify it with 'const auto' to directly pass the Source
        // obtained by the iterator to the output.
        // Otherwise you will get a 'const Source' with 'std::vector<const Source>::iterator'.
        auto &source1 = input["SOURCE1"].last().cast<std::vector<Source> >();
        auto &source2 = input["SOURCE2"].last().cast<std::vector<Source> >();

#ifdef DEBUG
        ptime start_time;
        ptime end_time;
        start_time = microsec_clock::local_time();
#endif

        std::vector<data> output_sources;

        if(this->handling_mode == HANDLING_MODE::IGNORE){
            for(std::vector<Source>::iterator it = source1.begin(); it != source1.end(); ++it){
                output_sources.push_back(data(*it));
            }
            for(std::vector<Source>::iterator it = source2.begin(); it != source2.end(); ++it){
                output_sources.push_back(data(*it));
            }
        }
        else if(this->handling_mode == HANDLING_MODE::PRIORITIZE_SOURCE1){
            std::vector<int> skip_ids;
            for(std::vector<Source>::iterator it = source1.begin(); it != source1.end(); ++it){
                skip_ids.push_back(it->id);
            }

            for(std::vector<Source>::iterator it = source1.begin(); it != source1.end(); ++it){
                output_sources.push_back(data(*it));
            }
            for(std::vector<Source>::iterator it = source2.begin(); it != source2.end(); ++it){
                if(*std::find(skip_ids.begin(), skip_ids.end(), it->id) != it->id){
                    output_sources.push_back(data(*it));
                }
            }
        }
        else if(this->handling_mode == HANDLING_MODE::PRIORITIZE_SOURCE2){
            std::vector<int> skip_ids;
            for(std::vector<Source>::iterator it = source2.begin(); it != source2.end(); ++it){
                skip_ids.push_back(it->id);
            }

            for(std::vector<Source>::iterator it = source1.begin(); it != source1.end(); ++it){
                if(*std::find(skip_ids.begin(), skip_ids.end(), it->id) != it->id){
                    output_sources.push_back(data(*it));
                }
            }
            for(std::vector<Source>::iterator it = source2.begin(); it != source2.end(); ++it){
                output_sources.push_back(data(*it));
            }
        }
        else if(this->handling_mode == HANDLING_MODE::USE_OFFSET){
            for(std::vector<Source>::iterator it = source1.begin(); it != source1.end(); ++it){
                Source src(*it);
                src.id += this->source1_offset;
                output_sources.push_back(data(src));
            }
            for(std::vector<Source>::iterator it = source2.begin(); it != source2.end(); ++it){
                Source src(*it);
                src.id += this->source2_offset;
                output_sources.push_back(data(src));
            }
        }
        else{
            std::stringstream ss;
            ss << "CombineSource: Unexpected this->handling_mode value '" << static_cast<int64_t>(this->handling_mode) << "', expected HANDLING_MODE::IGNORE='" << static_cast<int64_t>(HANDLING_MODE::IGNORE) << "', HANDLING_MODE::PRIORITIZE_SOURCE1='" << static_cast<int64_t>(HANDLING_MODE::PRIORITIZE_SOURCE1) << "', HANDLING_MODE::PRIORITIZE_SOURCE2='" << static_cast<int64_t>(HANDLING_MODE::PRIORITIZE_SOURCE2) << "' or HANDLING_MODE::USE_OFFSET='" << static_cast<int64_t>(HANDLING_MODE::USE_OFFSET) << "'.\n" << "FILE: " << __FILE__ << ", LINE: " << __LINE__;
            throw RuntimeError(ss.str());
        }

        output["SRCOUT"].push(data(std::move(output_sources)));

#ifdef DEBUG
        end_time = microsec_clock::local_time();
        std::cout << end_time - start_time << std::endl;
#endif

        return true;
    }
}
}

/* end of file */
