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

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

namespace hark
{
namespace node
{

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

    void
    SaveFrames::reset()
    {
    }
 
    void
    SaveFrames::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;
        }

        const std::string &output_type = input["OUTPUTTYPE"].last().cast<std::string>();
        const std::string &text_type = input["TEXTFORMAT"].last().cast<std::string>();
        this->fsio.set_modes(output_type, text_type);
    }

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

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

        if(inputData.is_type<std::vector<float> >()){
            const std::vector<float> &input_vec = inputData.cast<std::vector<float> >();
            this->fsio.writer_open(this->filename, "float", {input_vec.size()}, "");
            this->fsio.write(input_vec);
        }
        else if(inputData.is_type<std::vector<double> >()){
            const std::vector<double> &input_vec = inputData.cast<std::vector<double> >();
            this->fsio.writer_open(this->filename, "double", {input_vec.size()}, "");
            this->fsio.write(input_vec);
        }
        else if(inputData.is_type<std::vector<std::complex<float> > >()){
            const std::vector<std::complex<float> > &input_vec = inputData.cast<std::vector<std::complex<float> > >();
            this->fsio.writer_open(this->filename, "complex_float", {input_vec.size()}, "");
            this->fsio.write(input_vec);
        }
        else if(inputData.is_type<std::vector<std::complex<double> > >()){
            const std::vector<std::complex<double> > &input_vec = inputData.cast<std::vector<std::complex<double> > >();
            this->fsio.writer_open(this->filename, "complex_double", {input_vec.size()}, "");
            this->fsio.write(input_vec);
        }
        else if(inputData.is_type<std::vector<int> >()){
            const std::vector<int> &input_vec = inputData.cast<std::vector<int> >();
            this->fsio.writer_open(this->filename, "int", {input_vec.size()}, "");
            this->fsio.write(input_vec);
        }


        if(inputData.is_type<hark::Matrix<float> >()){
            const hark::Matrix<float> &input_mat = inputData.cast<hark::Matrix<float> >();
            this->fsio.writer_open(this->filename, "float", {input_mat.rows(), input_mat.cols()}, "");
            this->fsio.write(input_mat);
        }
        else if(inputData.is_type<hark::Matrix<double> >()){
            const hark::Matrix<double> &input_mat = inputData.cast<hark::Matrix<double> >();
            this->fsio.writer_open(this->filename, "double", {input_mat.rows(), input_mat.cols()}, "");
            this->fsio.write(input_mat);
        }
        else if(inputData.is_type<hark::Matrix<std::complex<float> > >()){
            const hark::Matrix<std::complex<float> > &input_mat = inputData.cast<hark::Matrix<std::complex<float> > >();
            this->fsio.writer_open(this->filename, "complex_float", {input_mat.rows(), input_mat.cols()}, "");
            this->fsio.write(input_mat);
        }
        else if(inputData.is_type<hark::Matrix<std::complex<double> > >()){
            const hark::Matrix<std::complex<double> > &input_mat = inputData.cast<hark::Matrix<std::complex<double> > >();
            this->fsio.writer_open(this->filename, "complex_double", {input_mat.rows(), input_mat.cols()}, "");
            this->fsio.write(input_mat);
        }
        else if(inputData.is_type<hark::Matrix<int> >()){
            const hark::Matrix<int> &input_mat = inputData.cast<hark::Matrix<int> >();
            this->fsio.writer_open(this->filename, "int", {input_mat.rows(), input_mat.cols()}, "");
            this->fsio.write(input_mat);
        }



        if(inputData.is_type<hark::Tensor<float> >()){
            const hark::Tensor<float> &input_ten = inputData.cast<hark::Tensor<float> >();
            this->fsio.writer_open(this->filename, "float", input_ten.vsize, "");
            this->fsio.write(input_ten);
        }
        else if(inputData.is_type<hark::Tensor<double> >()){
            const hark::Tensor<double> &input_ten = inputData.cast<hark::Tensor<double> >();
            this->fsio.writer_open(this->filename, "double", input_ten.vsize, "");
            this->fsio.write(input_ten);
        }
        else if(inputData.is_type<hark::Tensor<std::complex<float> > >()){
            const hark::Tensor<std::complex<float> > &input_ten = inputData.cast<hark::Tensor<std::complex<float> > >();
            this->fsio.writer_open(this->filename, "complex_float", input_ten.vsize, "");
            this->fsio.write(input_ten);
        }
        else if(inputData.is_type<hark::Tensor<std::complex<double> > >()){
            const hark::Tensor<std::complex<double> > &input_ten = inputData.cast<hark::Tensor<std::complex<double> > >();
            this->fsio.writer_open(this->filename, "complex_double", input_ten.vsize, "");
            this->fsio.write(input_ten);
        }
        else if(inputData.is_type<hark::Tensor<int> >()){
            const hark::Tensor<int> &input_ten = inputData.cast<hark::Tensor<int> >();
            this->fsio.writer_open(this->filename, "int", input_ten.vsize, "");
            this->fsio.write(input_ten);
        }


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

            for(auto it: input_map){


                int key = it.first;
                // generate filename by key.

                if(it.second.is_type<std::vector<float> >()){
                    const std::vector<float> &input_vec = it.second.cast<std::vector<float> >();
                    this->fsio.writer_open(this->filename, "float", {input_vec.size()}, "");
                    this->fsio.write(input_vec);
                }
                else if(it.second.is_type<std::vector<double> >()){
                    const std::vector<double> &input_vec = it.second.cast<std::vector<double> >();
                    this->fsio.writer_open(this->filename, "double", {input_vec.size()}, "");
                    this->fsio.write(input_vec);
                }
                else if(it.second.is_type<std::vector<std::complex<float> > >()){
                    const std::vector<std::complex<float> > &input_vec = it.second.cast<std::vector<std::complex<float> > >();
                    this->fsio.writer_open(this->filename, "complex_float", {input_vec.size()}, "");
                    this->fsio.write(input_vec);
                }
                else if(it.second.is_type<std::vector<std::complex<double> > >()){
                    const std::vector<std::complex<double> > &input_vec = it.second.cast<std::vector<std::complex<double> > >();
                    this->fsio.writer_open(this->filename, "complex_double", {input_vec.size()}, "");
                    this->fsio.write(input_vec);
                }
                else if(it.second.is_type<std::vector<int> >()){
                    const std::vector<int> &input_vec = it.second.cast<std::vector<int> >();
                    this->fsio.writer_open(this->filename, "int", {input_vec.size()}, "");
                    this->fsio.write(input_vec);
                }


                if(it.second.is_type<hark::Matrix<float> >()){
                    const hark::Matrix<float> &input_mat = it.second.cast<hark::Matrix<float> >();
                    this->fsio.writer_open(this->filename, "float", {input_mat.rows(), input_mat.cols()}, "");
                    this->fsio.write(input_mat);
                }
                else if(it.second.is_type<hark::Matrix<double> >()){
                    const hark::Matrix<double> &input_mat = it.second.cast<hark::Matrix<double> >();
                    this->fsio.writer_open(this->filename, "double", {input_mat.rows(), input_mat.cols()}, "");
                    this->fsio.write(input_mat);
                }
                else if(it.second.is_type<hark::Matrix<std::complex<float> > >()){
                    const hark::Matrix<std::complex<float> > &input_mat = it.second.cast<hark::Matrix<std::complex<float> > >();
                    this->fsio.writer_open(this->filename, "complex_float", {input_mat.rows(), input_mat.cols()}, "");
                    this->fsio.write(input_mat);
                }
                else if(it.second.is_type<hark::Matrix<std::complex<double> > >()){
                    const hark::Matrix<std::complex<double> > &input_mat = it.second.cast<hark::Matrix<std::complex<double> > >();
                    this->fsio.writer_open(this->filename, "complex_double", {input_mat.rows(), input_mat.cols()}, "");
                    this->fsio.write(input_mat);
                }
                else if(it.second.is_type<hark::Matrix<int> >()){
                    const hark::Matrix<int> &input_mat = it.second.cast<hark::Matrix<int> >();
                    this->fsio.writer_open(this->filename, "int", {input_mat.rows(), input_mat.cols()}, "");
                    this->fsio.write(input_mat);
                }



                if(it.second.is_type<hark::Tensor<float> >()){
                    const hark::Tensor<float> &input_ten = it.second.cast<hark::Tensor<float> >();
                    this->fsio.writer_open(this->filename, "float", input_ten.vsize, "");
                    this->fsio.write(input_ten);
                }
                else if(it.second.is_type<hark::Tensor<double> >()){
                    const hark::Tensor<double> &input_ten = it.second.cast<hark::Tensor<double> >();
                    this->fsio.writer_open(this->filename, "double", input_ten.vsize, "");
                    this->fsio.write(input_ten);
                }
                else if(it.second.is_type<hark::Tensor<std::complex<float> > >()){
                    const hark::Tensor<std::complex<float> > &input_ten = it.second.cast<hark::Tensor<std::complex<float> > >();
                    this->fsio.writer_open(this->filename, "complex_float", input_ten.vsize, "");
                    this->fsio.write(input_ten);
                }
                else if(it.second.is_type<hark::Tensor<std::complex<double> > >()){
                    const hark::Tensor<std::complex<double> > &input_ten = it.second.cast<hark::Tensor<std::complex<double> > >();
                    this->fsio.writer_open(this->filename, "complex_double", input_ten.vsize, "");
                    this->fsio.write(input_ten);
                }
                else if(it.second.is_type<hark::Tensor<int> >()){
                    const hark::Tensor<int> &input_ten = it.second.cast<hark::Tensor<int> >();
                    this->fsio.writer_open(this->filename, "int", input_ten.vsize, "");
                    this->fsio.write(input_ten);
                }




            }
        }

        else{
            std::stringstream ss;
            ss << "SaveFrames: Unexpected input type.";
            throw RuntimeError(ss.str());
        }

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

/* end of file */
