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

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

namespace hark
{
namespace node
{

    SaveVectorFrames::SaveVectorFrames():
        NodeDef("SaveVectorFrames")
    {
        *this
            << Terminal::in("INPUT")
            << Terminal::in("OUTPUTTYPE").def("TEXT")
            << Terminal::in("TEXTFORMAT").def("FIXED")
            << Terminal::in("FILENAME").def("")
            >> Terminal::out("OUTPUT");
    }

    void
    SaveVectorFrames::reset()
    {
    }
 
    void
    SaveVectorFrames::initialize(InputDataBundle &input)
    {
        this->filename = input["FILENAME"].last().cast<std::string>();
        if(filename.empty()){ throw RuntimeError("FILENAME is empty."); }
        this->will_format_filename = false;
        if(this->filename.find("{datatype", 0) != std::string::npos ||
           this->filename.find("{colskze", 0) != std::string::npos ||
           this->filename.find("{dim", 0) != std::string::npos){
            this->will_format_filename = true;
        }



        this->length = input["LENGTH"].last().cast<int>();
        this->wn_level = input["WN_LEVEL"].last().cast<int>();

        this->wn_factor = std::sqrt(this->length) / 2.0f;
        this->wn_coef = this->wn_factor * this->wn_level;

        this->gen = std::mt19937(1234);
        this->rand_uniform = std::uniform_real_distribution<float>(0.0f, 1.0f);
    }

    bool
    SaveVectorFrames::operator () (size_t count, InputDataBundle &input, OutputDataBundle &output)
    {
        if(count == 0){ this->initialize(input); }

        data inputData = input["INPUT"].last();
        data outputData;

        if(inputData.is_type<std::vector<float> >()){
            const std::vector<float> &input_vec = inputData.cast<std::vector<float> >();
            if(!this->fout.is_open()){
                std::string formatted_filename(this->filename);
                if(this->will_format_filename){
                    std::ostringstream oss;
                    oss << fmt::format(this->filename,
                                       fmt::arg("datatype", "float"),
                                       fmt::arg("colsize", input_vec.size()),
                                       fmt::arg("dim", 1));
                    formatted_filename = oss.str();
                }
                this->fout.open(formatted_filename, std::ios::binary);
                if(this->fout.fail()){
                    std::stringstream ss;
                    ss << "SaveVectorFrames: Can't open file '"
                       << formatted_filename << "' : " << __FILE__ << ", " << __LINE__;
                    throw RuntimeError(ss.str());
                }
            }
            if(this->mode == OutputMode::RAW){
                this->fout.write(reinterpret_cast<const char *>(input_vec.data()),
                                 sizeof(float) * input_vec.size());
            }
            else if(this->mode == OutputMode::TEXT){
                if(this->tmode == TextMode::FIXED){
                    this->fout << std::fixed << std::setprecision(std::numeric_limits<float>::max_digits10);
                }
                else if(this->tmode == TextMode::SCIENTIFIC){
                    this->fout << std::scientific << std::setprecision(std::numeric_limits<float>::max_digits10);
                }
                else{
                    ss << "SaveVectorFrames: Unexpected TextMode.\n"
                       << __FILE__ << ", " << __LINE__;
                    throw RuntimeError(ss.str());
                }
                for(size_t i = 0; i < input_vec.size(); i++){
                    this->fout << input_vec[i] << " ";
                }
                this->fout << std::endl;
            }
            else{
                std::stringstream ss;
                ss << "SaveVectorFrames: Unexpected OutputMode.\n"
                   << __FILE__ << ", " << __LINE__;
                throw RuntimeError(ss.str());
            }
        }
        else if(inputData.is_type<std::vector<double> >()){
            const std::vector<double> &input_vec = inputData.cast<std::vector<double> >();
            if(!this->fout.is_open()){
                std::string formatted_filename(this->filename);
                if(this->will_format_filename){
                    std::ostringstream oss;
                    oss << fmt::format(this->filename,
                                       fmt::arg("datatype", "double"),
                                       fmt::arg("colsize", input_vec.size()),
                                       fmt::arg("dim", 1));
                    formatted_filename = oss.str();
                }
                this->fout.open(formatted_filename, std::ios::binary);
                if(this->fout.fail()){
                    std::stringstream ss;
                    ss << "SaveVectorFrames: Can't open file '"
                       << formatted_filename << "' : " << __FILE__ << ", " << __LINE__;
                    throw RuntimeError(ss.str());
                }
            }
            if(this->mode == OutputMode::RAW){
                this->fout.write(reinterpret_cast<const char *>(input_vec.data()),
                                 sizeof(double) * input_vec.size());
            }
            else if(this->mode == OutputMode::TEXT){
                if(this->tmode == TextMode::FIXED){
                    this->fout << std::fixed << std::setprecision(std::numeric_limits<double>::max_digits10);
                }
                else if(this->tmode == TextMode::SCIENTIFIC){
                    this->fout << std::scientific << std::setprecision(std::numeric_limits<double>::max_digits10);
                }
                else{
                    ss << "SaveVectorFrames: Unexpected TextMode.\n"
                       << __FILE__ << ", " << __LINE__;
                    throw RuntimeError(ss.str());
                }
                for(size_t i = 0; i < input_vec.size(); i++){
                    this->fout << input_vec[i] << " ";
                }
                this->fout << std::endl;
            }
            else{
                std::stringstream ss;
                ss << "SaveVectorFrames: Unexpected OutputMode.\n"
                   << __FILE__ << ", " << __LINE__;
                throw RuntimeError(ss.str());
            }
        }

        if(inputData.is_type<std::vector<int> >()){
            const std::vector<int> &input_vec = inputData.cast<std::vector<int> >();
            if(!this->fout.is_open()){
                std::string formatted_filename(this->filename);
                if(this->will_format_filename){
                    std::ostringstream oss;
                    oss << fmt::format(this->filename,
                                       fmt::arg("datatype", "int"),
                                       fmt::arg("colsize", input_vec.size()),
                                       fmt::arg("dim", 1));
                    formatted_filename = oss.str();
                }
                this->fout.open(formatted_filename, std::ios::binary);
                if(this->fout.fail()){
                    std::stringstream ss;
                    ss << "SaveVectorFrames: Can't open file '"
                       << formatted_filename << "' : " << __FILE__ << ", " << __LINE__;
                    throw RuntimeError(ss.str());
                }
            }
            if(this->mode == OutputMode::RAW){
                this->fout.write(reinterpret_cast<const char *>(input_vec.data()),
                                 sizeof(int) * input_vec.size());
            }
            else if(this->mode == OutputMode::TEXT){
                if(this->tmode == TextMode::FIXED){
                    this->fout << std::fixed << std::setprecision(std::numeric_limits<float>::max_digits10);
                }
                else if(this->tmode == TextMode::SCIENTIFIC){
                    this->fout << std::scientific << std::setprecision(std::numeric_limits<float>::max_digits10);
                }
                else{
                    ss << "SaveVectorFrames: Unexpected TextMode.\n"
                       << __FILE__ << ", " << __LINE__;
                    throw RuntimeError(ss.str());
                }
                for(size_t i = 0; i < input_vec.size(); i++){
                    this->fout << input_vec[i] << " ";
                }
                this->fout << std::endl;
            }
            else{
                std::stringstream ss;
                ss << "SaveVectorFrames: Unexpected OutputMode.\n"
                   << __FILE__ << ", " << __LINE__;
                throw RuntimeError(ss.str());
            }
        }





        if(inputData.is_type<std::map<int, data> >()){
            const auto &inputMap = inputData.cast<std::map<int, data> >();

            outputData = data::create<std::map<int, data> >();
            auto &outputMap = outputData.cast<std::map<int, data> >();

            for(auto it: inputMap){
                const auto &vec = it.second.cast<std::vector<std::complex<float> > >();

                data outputVecData = data::create<std::vector<std::complex<float> > >(vec.size());
                std::vector<std::complex<float> > &output_vec = outputVecData.cast<std::vector<std::complex<float> > >();

                for(size_t i = 0; i < vec.size(); i++){
                    float theta = this->rand_uniform(gen) * this->coef;
                    output_vec[i] = (vec[i] + this->wn_coef * std::complex<float>(std::cos(theta), std::sin(theta)));
                }
                outputMap[it.first] = outputVecData;
            }
        }
        else{
            std::stringstream ss;
            ss << "PowerCalcForMap: Unexpected input type. Input must be hark::data(std::map<int, hark::data(std::vector<std::complex<float> >)>) .";
            throw RuntimeError(ss.str());
        }

        output["OUTPUT"].push(outputData);
        return true;
    }
}
}

/* end of file */
