/* 
 * File:   transformations.h
 * Author: pgess <v.melnychenko@xreate.org>
 *
 * Created on March 25, 2017, 9:04 PM
 */

#ifndef TRANSFORMATIONS_H
#define TRANSFORMATIONS_H

#include "pass/compilepass.h"

namespace xreate {    namespace compilation {

template <class TransformerType> 
struct TransformerInfo {
    //static const unsigned int id = 1; (current vacant id)
};
    
class Transformer{
public:
    virtual llvm::Value* transform(const Expression& expression, llvm::Value* raw, const Context& ctx)=0;
    virtual ~Transformer(){};
};

class TransformationsManager {
public:
    std::list<Transformer*> getRelevantTransformers(const Expression& expression);
    
    
    template<class TransformerType>
    void registerTransformer(const std::string& annotation, TransformerType* t){
        const int id = TransformerInfo<TransformerType>::id;

        assert(!__transformers.count(id));
        __transformers[id] = t;
        __subscriptions.emplace(annotation, id);
    }
    
    template<class TransformerType>
    void unregisterTransformer(const std::string& annotation, TransformerType* t){
        const unsigned int id = TransformerInfo<TransformerType>::id;

        auto range = __subscriptions.equal_range(annotation);
        const auto entry = make_pair(annotation, id);
        __subscriptions.erase(std::find_if(range.first, range.second, [id](const auto& el){return el.second == id;}));
        __transformers.erase(id);
    }
    
    template<class TransformerType>
    TransformerType* update(TransformerType* newInstance){
        const int id = TransformerInfo<TransformerType>::id;
        
        Transformer* oldInstance = __transformers[id];
        __transformers[id] = newInstance;
        
        return static_cast<TransformerType*>(oldInstance);
    }
    
    template<class TransformerType>
    bool exists(){
        const int id = TransformerInfo<TransformerType>::id;
        
        return __transformers.count(id);
    }
            
    template <class TransformerType>
    TransformerType* get(){
        const int id = TransformerInfo<TransformerType>::id;
        return static_cast<TransformerType*>(__transformers.at(id));
    }    
    
private:
    std::map<unsigned int, Transformer*> __transformers;
    std::multimap<std::string, unsigned int> __subscriptions;
};

template <class Parent>
class TransformationsScopeDecorator: public Transformer, public Parent {
                // SCOPE DECORATOR PART
public:    
    TransformationsScopeDecorator(CodeScope* codeScope, FunctionUnit* f, CompilePass* compilePass)
        : Parent(codeScope, f, compilePass){}
    
    virtual llvm::Value* 
    process(const Expression& expr, const std::string& hintVarDecl=""){
        llvm::Value* result = Parent::process(expr, hintVarDecl);

        return transform(expr, result, Context{this, Parent::function, Parent::pass});
    }
  
                // TRANSFORMER PART
public:
    virtual llvm::Value* 
    transform(const Expression& expression, llvm::Value* raw, const Context& ctx) {
        llvm::Value* result = raw;
        TransformationsManager* man = Parent::pass->managerTransformations;
        
        if (expression.tags.size())
        for (Transformer* handler: man->getRelevantTransformers(expression)){
            result = handler->transform(expression, result, ctx);
        }
        
        return result;
    }
};
    
} } 

#endif /* TRANSFORMATIONS_H */

