#ifndef linearGeneratorWrapper_h
#define linearGeneratorWrapper_h

#include <memory>
#include <string>
#include <vector>
#include <list>
#include <map>

#ifndef R_NO_REMAP
#define R_NO_REMAP
#endif
#include <Rinternals.h>
#include <R.h>

class LinearExtensionGenerator;
class LEGBubleyDyer;
class POSetWrap;
class FunctionLinearExtension;

class LinearGeneratorWrap {
protected:
    bool finite;
public:
    enum class LEGType {
        BubleyDyer,
        TreeOfIdeals,
        BinaryVariable,
        FromLinearPosets,
        Count
    };
    
    std::map<std::string, LEGType> linearExtensionGeneratorMapType {
        { "BubleyDyer", LEGType::BubleyDyer },
        { "TreeOfIdeals", LEGType::TreeOfIdeals },
        { "FromLinearPosets", LEGType::FromLinearPosets },
    };
    
    LinearGeneratorWrap() {generator = nullptr; finite = false;}
public:
    std::shared_ptr<LinearExtensionGenerator> generator;
    LEGType type;
    ~LinearGeneratorWrap() {}
    static LinearGeneratorWrap* BuildBubleyDyerGenerator(POSetWrap*, std::map<std::string, std::uint_fast64_t>&);
    static LinearGeneratorWrap* BuildLEGenerator(POSetWrap*);
    static LinearGeneratorWrap* BuildBubleyDyerMRPObject(POSetWrap*);

    std::shared_ptr<std::list<std::shared_ptr<std::vector<std::string>>>> GetFromLE(bool, std::uint_fast64_t, std::uint_fast64_t);
    std::shared_ptr<std::list<std::shared_ptr<std::vector<std::string>>>> GetFromBubleyDyer(bool, std::uint_fast64_t, std::shared_ptr<double>, std::uint_fast64_t);
    static SEXP BuildExactEvaluation(POSetWrap*, std::vector<std::string>&, std::vector<SEXP>&, std::uint_fast64_t, int&);
    
    std::uint_fast64_t LESize() const;
private:
    std::shared_ptr<std::list<std::shared_ptr<std::vector<std::string>>>> get(bool, std::uint_fast64_t, std::uint_fast64_t);
};

class BubleyDyerMRPGenerator {
public:
    std::shared_ptr<LEGBubleyDyer> le_generator;
    std::shared_ptr<Matrice<double>> mrp;
    bool used;

    BubleyDyerMRPGenerator() {le_generator = nullptr; used = false;}
    ~BubleyDyerMRPGenerator() {}
    static BubleyDyerMRPGenerator* BuildBubleyDyerMRPGenerator(POSetWrap* poset, std::shared_ptr<Random> rnd);
    SEXP BuildMRP(std::uint_fast64_t, std::shared_ptr<double>, std::uint_fast64_t, int&);
};

class BubleyDyerEvaluationGenerator {
public:
    std::shared_ptr<LEGBubleyDyer> le_generator;
    std::vector<std::shared_ptr<Matrice<double>>> eval_results;
    std::vector<std::shared_ptr<FunctionLinearExtension>> fles;
    std::vector<std::string> functions_name;
    bool used;
    BubleyDyerEvaluationGenerator() {le_generator = nullptr; used = false;}
    ~BubleyDyerEvaluationGenerator() {}
    static BubleyDyerEvaluationGenerator* BuildBubleyDyerEvaluationGenerator(POSetWrap* poset,
                                                                             std::shared_ptr<Random> rnd,
                                                                             std::vector<std::string>& internal_functions,
                                                                             std::vector<SEXP>& external_functions);
    SEXP Evaluation(std::uint_fast64_t, std::shared_ptr<double>, std::uint_fast64_t, int&);
};

#endif /* linearGeneratorWrapper_h */
