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

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

namespace hark
{
namespace node
{
    CMLoad::CMLoad():
        NodeDef("CMLoad")
    {
        *this
            << Terminal::in("FILENAME")
            << Terminal::in("OPERATION_FLAG").def(false)
            << Terminal::in("ENABLE_DEBUG").def(false)
            >> Terminal::out("OUTPUT");
    }

    void
    CMLoad::reset()
    {
    }

    void
    CMLoad::initialize(InputDataBundle &input)
    {
        this->current_filename = "";
    }

    void CMLoad::loadCM(std::string &filename)
    {
        int _size, _rows, _cols;

        harkio_TransferFunction *mat = nullptr;
        mat = harkio_TransferFunction_fromFile(const_cast<char*>(filename.c_str()), const_cast<char*>("correlation matrix"));
        COMPLEX_TYPE complexValue;
        this->Rxx.resize(mat->poses->size, mat->loctfs[0]->rows * mat->loctfs[0]->cols);
        for(int k = 0; k < mat->poses->size; k++){
            for(int i = 0; i < mat->loctfs[0]->rows; i++){
                for(int j = 0; j < mat->loctfs[0]->cols; j++){
                    harkio_Matrix_getValueComplex(mat->loctfs[k], i, j, &complexValue);
                    Rxx(k, i * mat->loctfs[0]->cols + j) = complexValue;
                }
            }
        }
        _size = mat->poses->size;
        _rows = mat->loctfs[0]->rows;
        _cols = mat->loctfs[0]->cols;
        harkio_TransferFunction_delete(&mat);
        this->pslength = _size;
        this->nb_channels = static_cast<int>(std::floor(std::sqrt(_cols * _rows)));
        if(this->nb_channels != _cols || this->nb_channels != _rows){
            std::stringstream ss;
            ss << "CMLoad: Incorrect correlation matrix size.\n" << "FILE: " << __FILE__ << ", LINE: " << __LINE__;
            throw RuntimeError(ss.str());
        }
    }

    bool
    CMLoad::operator () (size_t count, InputDataBundle &input, OutputDataBundle &output)
    {
        ////const auto &operation_flag = input["OPERATION_FLAG"].last().as<bool>();

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

        const auto &operation_flag = input["OPERATION_FLAG"].last().as<bool>();

        if(count == 0){ this->initialize(input); }

        if((count == 0) || operation_flag){
            std::string filename = input["FILENAME"].last().cast<std::string>();
            if(filename.empty()){
                std::stringstream ss;
                ss << "CMLoad: No file name is given.\n" << "FILE: " << __FILE__ << ", LINE: " << __LINE__;
                throw RuntimeError(ss.str());
            }
            if(this->current_filename != filename){
                this->current_filename.swap(filename);
            }
            this->loadCM(this->current_filename);
        }

        auto out = data::create<Matrix<std::complex<float> > >(this->pslength, this->nb_channels * this->nb_channels);
        auto &outputcm = out.cast<Matrix<std::complex<float> > >();
        outputcm.noalias() = Rxx;

        output["OUTPUT"].push(out);

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

        return true;
    }
}
}

/* end of file */
