/* 
 * File:   scopedecorators.h
 * Author: pgess <v.melnychenko@xreate.org>
 *
 * Created on February 24, 2017, 11:35 AM
 */

#ifndef SCOPEDECORATORS_H
#define SCOPEDECORATORS_H

#include "ast.h"
#include "compilation/targetinterpretation.h"
#include "compilation/versions.h"

namespace xreate { 
    
class CompilePass;

namespace compilation {

class AbstractCodeScopeUnit;
class FunctionUnit;

template<class Parent>
class CachedScopeDecorator: public Parent{
    typedef CachedScopeDecorator<Parent> SELF;

public:
    CachedScopeDecorator(CodeScope* codeScope, FunctionUnit* f, CompilePass* compilePass): Parent(codeScope, f, compilePass){}

    void reset(){
        __rawVars.clear();
    }
    
    void bindArg(llvm::Value* value, std::string&& alias)
    {
        //ensure existence of an alias
        assert(Parent::scope->__identifiers.count(alias));

        //memorize new value for an alias
        ScopedSymbol id{Parent::scope->__identifiers.at(alias), VERSION_NONE};
        __rawVars[id] = value;
    }

    void bindArg(llvm::Value* value, const ScopedSymbol& s) {
        __rawVars[s] = value;
    }
    
    llvm::Value* compile(const std::string& hintBlockDecl="") override{
        if (__rawVars.count(ScopedSymbol::RetSymbol)){
            return __rawVars[ScopedSymbol::RetSymbol];
        }
        
        return Parent::compile(hintBlockDecl);
    }

    llvm::Value* 
    processSymbol(const Symbol& s, std::string hintRetVar) override{
        CodeScope* scope = s.scope;
        SELF* self = dynamic_cast<SELF*>(Parent::function->getScopeUnit(scope));

        if (self->__rawVars.count(s.identifier)){
            return self->__rawVars[s.identifier];
        }

        //compilation transformations could override symbol declarations.
        Expression declaration = CodeScope::getDeclaration(s);
        if (!declaration.isDefined()){
            if (self->__declarationsOverriden.count(s.identifier)){
                declaration = self->__declarationsOverriden[s.identifier];

            } else {
                assert(false); //in case of bindings there should be raws already.
            }
        }

        return self->__rawVars[s.identifier] = Parent::processSymbol(s, hintRetVar);
    }

    void 
    overrideDeclaration(const Symbol binding, Expression&& declaration){
        SELF* self = dynamic_cast<SELF*>(Parent::function->getScopeUnit(binding.scope));

        self->__declarationsOverriden.emplace(binding.identifier, std::move(declaration));
    }

private:
    std::unordered_map<ScopedSymbol, Expression> __declarationsOverriden;
    std::unordered_map<ScopedSymbol,llvm::Value*> __rawVars;

};

typedef CachedScopeDecorator<
        InterpretationScopeDecorator<
        VersionsScopeDecorator<BasicCodeScopeUnit>>>

        DefaultScopeUnit;

} //end of compilation namespace

struct CachedScopeDecoratorTag;
struct VersionsScopeDecoratorTag;

template<>
struct DecoratorsDict<CachedScopeDecoratorTag>{
    typedef compilation::CachedScopeDecorator<
            compilation::InterpretationScopeDecorator<
            compilation::VersionsScopeDecorator<compilation::BasicCodeScopeUnit>>> result;
};

template<>
struct DecoratorsDict<VersionsScopeDecoratorTag>{
    typedef compilation::VersionsScopeDecorator<
            compilation::VersionsScopeDecorator<compilation::BasicCodeScopeUnit>> result;
};

} //end of xreate

#endif /* SCOPEDECORATORS_H */

