// $Header: https://oberon.oapd.inaf.it/shins/trunk/shins/src/tiptilt_ctrl/bcu/util/rtcDM.hpp 3975 2022-06-14 18:34:48Z depa $

/**
 *   \file rtcDM.h
 *   \brief A Documented file.
 *
 *  Detailed description
 *
 */

#pragma once

#include <algorithm>
#include <string>
#include <fstream>
#include <sstream>

#include <boost/filesystem.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/shared_ptr.hpp>

#include <CCfits>
#include <Eigen/Core>

#include <Nice/BuiltinSequences.h>

#include "util/shins_util.h"
#include "util/logger.h"

namespace su = shins::util;
namespace sud = shins::util::defaults;
namespace shins
{
namespace util
{

class rtcDM
{
private:
    bool m_zonalMode;
    Eigen::MatrixXf m_influenceMatrix;
    Nice::SeqSeqFloat m_influenceMatrix2;
    Nice::SeqFloat m_modeWeights;
    Nice::SeqInt m_actuatorMap;
    Nice::SeqFloat m_flatMap;
    Nice::SeqFloat m_actuatorSignMap;
    float m_maxPower;

    template <typename T>
    void load_one_col_map(const std::string& _fileName, std::vector<T>& _vecOut);

//    template <typename Derived>
//    void load_matrix_map(const std::string& _fileName, Eigen::MatrixBase<Derived> & _mat);

    template <typename T>
    T get_value_from_map(const std::map<std::string, std::string>& _mapSetup, const std::string& _key);
public:
    rtcDM();
    rtcDM(const std::map<std::string, std::string>& _mapSetup);
    ~rtcDM();

    void update_flat(const std::string & _strFlat);
    void init_modalMode(const std::string & _strMode2commands);   // modal constructor
    float get_mode_weight(int index)  const;

    void init_zonalMode();   // zonal contructor

    inline unsigned int get_length_of_modes_vector() const{
        return static_cast<unsigned int>(std::ceil((m_influenceMatrix.rows() + 2.)/8.)*8);
    }
    inline unsigned int get_length_of_modes_vector_const() const {
        return static_cast<unsigned int>(std::ceil((m_influenceMatrix.rows() + 2.)/8.)*8);
    }
    inline unsigned int get_raw_number_of_modes() const{
        return m_influenceMatrix.rows();
    }
    inline unsigned int get_in_use_actuator_number() const {
        return static_cast<unsigned int>(std::ceil(m_influenceMatrix.cols()/8.)*8);
    }                                                                                    // number of actuators used.
    inline unsigned int get_number_of_actuators() const {
        return static_cast<unsigned int>(std::ceil(m_influenceMatrix.cols()/64.)*64);
    }
    inline Nice::SeqFloat get_max_power() const {
        return Nice::SeqFloat(1, m_maxPower);
    }
    void set_max_power(float mp){m_maxPower = mp;};
    inline Nice::SeqInt get_actuator_map() const {
        return m_actuatorMap;
    }

    inline bool is_zonal_mode() const {
        return m_zonalMode;
    }

    Nice::SeqFloat get_flat() const;
    Nice::SeqFloat get_signmap_for_bcu() const;
    Nice::SeqInt get_in_use_actuator_number_for_bcu() const;
    Nice::SeqInt get_number_of_actuators_for_bcu() const;
    Nice::SeqSeqFloat get_influence_matrix() const;
    Nice::SeqSeqFloat get_influence_matrix2() const;

    Nice::Int update_m2c(std::string _filename);
    Nice::Int load_m2c_fits_file(std::string _filename, std::vector<std::vector<double> > & _m2c);
    Nice::Int load_m2c_dat_file(const std::string & _datFile, std::vector< std::vector<double> > &  _m2c);

};


    template <typename T>
    void rtcDM::load_one_col_map(const std::string& _fileName, std::vector<T>& _vecOut)
    {
        U6_LLOG("rtcDM.hpp::rtcDM::" << __FUNCTION__);
        int i = 0;
        std::istringstream isstream;
        std::ifstream ifstrm;
        std::string line = "";
        T tmpT;

        if(boost::filesystem::exists(boost::filesystem::path(_fileName)))
        {
            ifstrm.open(_fileName.c_str());
            while (std::getline(ifstrm, line) && !isstream.fail())
            {
                isstream.clear();
                isstream.str(line);
                isstream >> tmpT;
                if(!isstream.fail())
                {
//                    U9_LLOG("Line " << i + 1 << " : " << line << "; RDSTATE : " << isstream.rdstate());
                    _vecOut.push_back(tmpT);
                }
                else
                {
//                    U9_LLOG("Error in line " << i + 1 << " : " << line << "; RDSTATE : " << isstream.rdstate())
                }
                i++;
                line="";
            }
            i=0;
            ifstrm.close();
        }
        else
        {
            A_LLOG("rtcDM.hpp::rtcDM::" << __FUNCTION__ << " ~ File " << _fileName << " does not exists");
            U8_LLOG("");
            throw (std::invalid_argument("File " + _fileName + " does not exists"));
        }
        U8_LLOG("");
    }

    template <typename T>
    T rtcDM::get_value_from_map(const std::map<std::string, std::string>& _mapSetup, const std::string& _key)
    {
        U6_LLOG("rtcDM.hpp::rtcDM::" << __FUNCTION__);
        T value = T();
        try
        {
            value = shins::util::get_value_from_map<T, std::map<std::string, std::string>, std::string>(_mapSetup, _key);
        }
        catch(std::invalid_argument& e)
        {
            W_LLOG(e.what());
            U8_LLOG("");
            throw e;
        }
        U8_LLOG("");
        return value;
    }

    typedef boost::shared_ptr<rtcDM> rtcDMPtr;
  }
}
