
#ifndef tranformExtension_hpp
#define tranformExtension_hpp

#include <memory>
#include <map>
#include <vector>

#include "paramType.h"
#include "linearExtension.h"

// ***********************************************
// ***********************************************
// ***********************************************

class TranformExtension {
protected:
    std::uint64_t __calls;
    std::shared_ptr<std::vector<std::string>> __pos_labels;
    std::map<std::string, std::uint64_t> __labels_pos;
public:
    TranformExtension(std::shared_ptr<std::vector<std::string>> pos_labels) : __pos_labels(pos_labels) {
    }
    virtual ~TranformExtension() {};
    std::shared_ptr<std::vector<std::string>> Elements() {
        return __pos_labels;
    }
    virtual std::shared_ptr<LinearExtension> operator()(std::shared_ptr<LinearExtension> x) = 0;
    virtual std::string to_string() const = 0;
};


// ***********************************************
// ***********************************************
// ***********************************************

class TEItentity : public TranformExtension {
protected:
public:
    TEItentity(std::shared_ptr<std::vector<std::string>> pos_labels) : TranformExtension(pos_labels) {
        __calls = 0;
        for (std::uint64_t eid = 0; eid < __pos_labels->size(); ++eid) {
            auto p = __pos_labels->at(eid);
            __labels_pos[p] = eid;
        }
    }
    virtual ~TEItentity() {};
    std::shared_ptr<LinearExtension> operator()(std::shared_ptr<LinearExtension> x) {
        ++__calls;
        return x;
    }
    virtual std::string to_string() const {return "";};
};

// ***********************************************
// ***********************************************
// ***********************************************

class TELexicographical : public TranformExtension { 
private:
    std::vector<std::uint64_t>& __modalities;
    std::uint64_t __total_elements;
    std::vector<std::vector<std::string>> __result_vector;
public:
    TELexicographical(std::shared_ptr<std::vector<std::string>> pos_labels, std::vector<std::uint64_t>& modalities) : TranformExtension(pos_labels), __modalities(modalities) {
        __calls = 0;
        __total_elements = 1;
        for (std::uint64_t k = 0; k < __modalities.size(); ++k) {
            __total_elements *= __modalities.at(k);
        }
        
        __result_vector.resize(__total_elements);
        for (std::uint64_t k = 0; k < __result_vector.size(); ++k) {
            __result_vector.at(k).resize(__modalities.size());
        }
        
        __pos_labels = std::make_shared<std::vector<std::string>>(__total_elements);
        
        std::uint64_t change_at = __total_elements;
        for (std::uint64_t k = 0; k < __modalities.size(); ++k) {
            std::uint64_t m_size = __modalities.at(k);
            change_at = change_at / m_size;
            for (std::uint64_t h = 0, c = 0; h < __result_vector.size(); ++h, ++c) {
                std::uint64_t val_m_p = (c / change_at) % m_size;
                __result_vector.at(h).at(k) = std::to_string(val_m_p);
            }
        }
        for (std::uint64_t k = 0; k < __result_vector.size(); ++k) {
            std::string value = "";
            for (std::uint64_t h = 0; h < __result_vector.at(k).size(); ++h) {
                if (h < __result_vector.at(k).size() - 1)
                    value += __result_vector.at(k).at(h) + "-";
                else
                    value += __result_vector.at(k).at(h);
            }
            (*__pos_labels)[k] = value;
            __labels_pos[value] = k;
        }

        
    }
    virtual ~TELexicographical() {};
    std::shared_ptr<LinearExtension> operator()(std::shared_ptr<LinearExtension> x) {
        std::uint64_t change_at = __total_elements;
        for (std::uint64_t k = 0; k < __modalities.size(); ++k) {
            std::uint64_t m = x->getVal(k);
            std::uint64_t m_size = __modalities.at(m);
            change_at = change_at / m_size;
            
            for (std::uint64_t h = 0, c = 0; h < __result_vector.size(); ++h, ++c) {
                std::uint64_t val_m_p = (c / change_at) % m_size;
                //std::string val_m = __modalities.at(m).at(val_m_p);
                //__result_vector.at(h).at(m) = val_m;
                __result_vector.at(h).at(m) = std::to_string(val_m_p);

            }
        }
        
        std::shared_ptr<LinearExtension> result = std::make_shared<LinearExtension>(__result_vector.size());
        for (std::uint64_t k = 0; k < __result_vector.size(); ++k) {
            std::string value = "";
            for (std::uint64_t h = 0; h < __result_vector.at(k).size(); ++h) {
                if (h < __result_vector.at(k).size() - 1)
                    value += __result_vector.at(k).at(h) + "-";
                else
                    value += __result_vector.at(k).at(h);
            }
            std::uint64_t pos = __labels_pos[value];

            result->set(k, pos);
        }
        return result;
    }
    virtual std::string to_string() const {return "";};
};

// ***********************************************
// ***********************************************
// ***********************************************

#endif /* tranformEstention_hpp */
