/* 
 * File:   Transformations.h
 * Author: pgess
 *
 * Created on June 18, 2016, 6:23 PM
 */

#ifndef TRANSFORMATIONS_H
#define TRANSFORMATIONS_H

#include "ast.h"
#include "pass/compilepass.h"
#include "attachments.h"

namespace llvm {
    class BasicBlock;
}

namespace xreate {    namespace compilation {
    
    class Transformer{
    public:
        virtual llvm::Value* transform(const Expression& expression, llvm::Value* raw, const Context& ctx)=0;
        virtual Expression transform(const Expression& expression, const Context& ctx){return expression;}
        virtual bool isAcceptable(const Expression& expression)=0;
        
        virtual ~Transformer(){};
    };
    
    class TransformerSaturation: public Transformer{
    public:    
        llvm::Value* transform(const Expression& expression, llvm::Value* raw, const Context& ctx);
        bool isAcceptable(const Expression& expression);

        TransformerSaturation(Transformations*);
        llvm::BasicBlock* getBlock() const;
        bool exists() const;
        void inject(llvm::BasicBlock* blockAllocation, llvm::BasicBlock* blockExit, compilation::Context context);
        
    private:
        llvm::BasicBlock* __block = nullptr;
        Transformations* __man;
    };
    
    template <class TransformerType> 
    struct TransformerInfo {
        //static const int id = 1; (next vacant id)
    };
    
    template <> 
    struct TransformerInfo<TransformerSaturation> {
        static const int id = 0;
    };
    
    class Transformations;
    
    struct SubscriberInfo{
        bool flagSubscribed;
        int subscriberId;
    };
    
    class Transformations: public Transformer {
    public:
        xreate::CompilePass* pass;
        
        bool isAcceptable(const Expression& expression);
        llvm::Value* transform(const Expression& expression, llvm::Value* raw, const Context& ctx) override;
        Expression transform(const Expression& expression, const Context& ctx) override;
        
        Transformations(CompilePass*);
        void subscribe(const std::string& annotation, int handler);
        
        template<class TransformerType>
        void registerTransformer(TransformerType* t){
            const int id = TransformerInfo<TransformerType>::id;
            
            __transformers[id] = t;
        }
        
        template<class Holder, class TransformerType>
        static void  subscribe(const Holder& holder){
            //assert(! Attachments::exists<Holder, Transformations>());
            
            const int id = TransformerInfo<TransformerType>::id;
            
            if (Attachments::exists<Holder, Transformations>(holder)){
                const int idOld = Attachments::get<Holder, Transformations>(holder).subscriberId;
                assert(idOld == id);
                return;
            }
            
            Attachments::put<Holder, Transformations>(holder, {true, id});
        }

        template <class TransformerType>
        TransformerType* get(){
            const int id = TransformerInfo<TransformerType>::id;
            return static_cast<TransformerType*>(__transformers.at(id));
        }

    private:
        std::map<int, Transformer*> __transformers;
        std::multimap<std::string, int> __subscriptions;
        
        std::list<Transformer*> getAppropriateTransformers(const Expression& expression);
    };
}}

namespace xreate {
    template<>
    struct AttachmentsDict<compilation::Transformations>
    {
        typedef xreate::compilation::SubscriberInfo Data;
        static const unsigned int key = 4;
    };
}
    
#endif /* TRANSFORMATIONS_H */

