
/*
 * versions.cpp
 *
 * Author: pgess <v.melnychenko@xreate.org>
 * Created on January 21, 2017, 1:24 PM
 */

#include "pass/versionspass.h"
#include "pass/compilepass.h"

namespace xreate {

class CompilePass;

namespace compilation {

class AbstractCodeScopeUnit;
class FunctionUnit;

template<class Parent>
class VersionsScopeDecorator: public Parent{
    typedef VersionsScopeDecorator<Parent> SELF;
    
public:
    VersionsScopeDecorator(CodeScope* codeScope, FunctionUnit* f, CompilePass* compilePass): Parent(codeScope, f, compilePass){}

    virtual llvm::Value* processSymbol(const Symbol& s, std::string hintSymbol=""){
        if (Attachments::exists<VersionImposedDependency>(s)){
            const std::list<Symbol> dependencies = Attachments::get<VersionImposedDependency>(s);

            for(const Symbol& symbolDependent: dependencies){
                processSymbol(symbolDependent);
            }
        }

        llvm::Value* result = Parent::processSymbol(s, hintSymbol);
        
        if (s.identifier.version == VERSION_INIT){
            llvm::Value* storage = SELF::processIntrinsicInit(result->getType(), hintSymbol);
            setSymbolStorage(s, storage);
            
            processIntrinsicCopy(result, storage);
            return AbstractCodeScopeUnit::pass->man->llvm->builder.CreateLoad(storage);
            
        } else if (s.identifier.version != VERSION_NONE){
            Symbol symbolInitVersion = getSymbolInitVersion(s);
            
           llvm::Value* storage = getSymbolStorage(symbolInitVersion);
           processIntrinsicCopy(result, storage);
           
           return AbstractCodeScopeUnit::pass->man->llvm->builder.CreateLoad(storage);
        }
        
        return result;
    }
    
    llvm::Value* 
    processIntrinsicInit(llvm::Type* typeStorage, const std::string& hintVarDecl=""){
        llvm::IntegerType* tyInt = llvm::Type::getInt32Ty(llvm::getGlobalContext());
        llvm::ConstantInt* constOne = llvm::ConstantInt::get(tyInt, 1, false);
        
        return AbstractCodeScopeUnit::pass->man->llvm->builder.CreateAlloca(typeStorage, constOne, hintVarDecl);
    }
    
    void
    processIntrinsicCopy(llvm::Value* value, llvm::Value* storage){
        AbstractCodeScopeUnit::pass->man->llvm->builder.CreateStore(value, storage);
    }
    
private:
    std::map<Symbol, llvm::Value*> __symbolStorage;
    
        static Symbol
    getSymbolInitVersion(const Symbol& s){
        return Symbol{ScopedSymbol{s.identifier.id, VERSION_INIT}, s.scope};
    }
    
    llvm::Value*
    getSymbolStorage(const Symbol& s){
        return __symbolStorage.at(s);
    }
    
    void setSymbolStorage(const Symbol& s, llvm::Value* storage){
        __symbolStorage[s] = storage;
    }

};
}   //end of compilation namespace
} //end of xreate namespace


//    llvm::Value*
//    processIntrinsicInitAndCopy(){
//         
//    }

//llvm::Value*
//process(const Expression& expr, const std::string& hintVarDecl){
//    case Operator::CALL_INTRINSIC: {
//        enum INRINSIC{INIT, COPY};
//        
//        const ExpandedType& typSymbol = pass->man->root->expandType(expr.type);
//
//        INTRINSIC op = (INTRINSIC) expr.getValueDouble();
//
//        switch (op){
//            case INIT: {
//                llvm::Type* typSymbolRaw = l.toLLVMType(typSymbol);
//                
//
//                return storage;
//            }
//
//            case COPY: {
//                llvm::Type* typSymbolRaw = l.toLLVMType(typSymbol);
//                llvm::value* valueOriginal = process(expr.getOperands()[0], hintVarDecl);
//                llvm::Value* storage = l.builder.CreateAlloca(typSymbolRaw, constOne, hintVarDecl);
//                llvm::Value* valueCopy = l.builder.CreateStore(valueOriginal, storage);
//
//                return valueCopy;
//            }
//        }
//        return;
//    }
//}
//};