/*
 * LateContext.cpp
 *
 *  Created on: Jan 8, 2016
 *      Author: pgess
 */

#include "query/context.h"
#include <compilation/latecontextcompiler.h>
#include "pass/compilepass.h"
#include "llvmlayer.h"

namespace xreate {

LateContextCompiler::LateContextCompiler(const std::string& function, CompilePass* compilePass)
	: pass(compilePass)
{}

LateContextCompiler::~LateContextCompiler()
{}

llvm::Value*
LateContextCompiler::compileCheckContext(const Expression& e){
//	llvm::Value* environment;
//	LLVMLayer* llvm;
//
//	PackedExpression rep = __context.pack(e);
//	assert(storage.count(rep));
//
//	size_t id = storage.at(rep);
//	//TODO determine short name of expression
//	return llvm->builder.CreateExtractElement(environment, id, NAME("checkContext"+e.getValueString()));

	return nullptr;
}
	
llvm::Value*
LateContextCompiler::findFunction(const std::string name, ScopePacked scopeOuter, llvm::Function* functionOuter){
/*
 * , functionDomain(&compilePass->queryContext->getFunctionDomain(function))
											//RUNTIME PART
	llvm::BasicBlock *blockCurrent = llvm->builder.GetInsertBlock();
	llvm::BasicBlock *blockEnd = llvm::BasicBlock::Create(llvm::getGlobalContext(), "ContextDeterminationEnd", functionOuter);

	llvm->builder.SetInsertPoint(blockEnd);
	llvm::Type* tyFuncCallee = funcDefault->getType();
	llvm::PHINode *ret =  llvm->builder.CreatePHI(tyFuncCallee, functionDomain->size(), "callee");


	//check runtime context
	for (const ManagedFnPtr& f: specializations){
		const Expression& guard = f->guardContext;
		if (!guard.isValid()) continue;

		if (functionDomain->count(guard)){
			///example: if (domain[c]) return specializtion.at(c) else
			llvm->builder.SetInsertPoint(blockCurrent);
				//TASK implement `cond`
			llvm::Value* cond;
			llvm::BasicBlock *blockNext = llvm::BasicBlock::Create(llvm::getGlobalContext(), "ContextDeterminationNext", functionOuter);
			llvm->builder.CreateCondBr(cond, blockEnd, blockNext);
			ret->addIncoming(pass->getFunctionUnit(f)->compile(), blockCurrent);
			blockCurrent = blockNext;
		}
	}

	//check default variant in runtime
	llvm->builder.SetInsertPoint(blockCurrent);
	llvm->builder.CreateBr(blockEnd);
	ret->addIncoming(funcDefault, blockCurrent);

	llvm->builder.SetInsertPoint(blockEnd);
	return ret;

	*/ return nullptr;
}

llvm::Value*
LateContextCompiler::compileArgument(const std::string& callee, ScopePacked scopeOuter){
	/*
	LLVMLayer* llvm = pass->man->llvm;

	std::unordered_set<size_t> indexContextStatic;
	const ContextDomain& listContextStatic = pass->queryContext->getContext(scopeOuter);
	const ContextDomain& calleeDomain = pass->queryContext->getFunctionDomain(callee);

	llvm::Type* tyBool = llvm::Type::getInt1Ty(llvm::getGlobalContext());
	llvm::Type* tyInt32 = llvm::Type::getInt32Ty(llvm::getGlobalContext());

	llvm::Value* result = llvm->builder.CreateAlloca(tyBool, llvm::ConstantInt::get(tyInt32, calleeDomain.size()));
	llvm::Value* valueTrue = llvm::ConstantInt::get(tyBool, 1);
	llvm::Value* valueFalse = llvm::ConstantInt::get(tyBool, 0);

	for (const Expression& e: listContextStatic){
		auto i = calleeDomain.getId(e); //TODO it is like iteration 0..size-1, right?
		if (i){
			result = llvm->builder.CreateInsertElement(result, llvm::ConstantInt::get(tyInt32, *i), valueTrue);
			indexContextStatic.insert(*i);
		}
	}

	for (size_t i=0, size=calleeDomain.size(); i<size; ++i){
		const Expression& e = calleeDomain.get(i);
		if (indexContextStatic.count(i))
			continue;

		auto posPrev = functionDomain->getExpressionId(e);
		if (posPrev){
			llvm::Value* element = llvm->builder.CreateExtractElement(raw, llvm::ConstantInt::get(tyInt32, *posPrev));
			result = llvm->builder.CreateInsertElement(result, llvm::ConstantInt::get(tyInt32, i), element);
			continue;
		}

		result = llvm->builder.CreateInsertElement(result, llvm::ConstantInt::get(tyInt32, i), valueFalse);
	}

	return result;
	*/

	return nullptr;
}

size_t
LateContextCompiler::getFunctionDomainSize() const {
	return pass->queryContext->getFunctionDomain(functionName).size();
}

//void
//LateContext::set(const Expression& e){
//	PackedExpression representation = 	__context.pack(e);
//
//	assert(__context.count(representation) == 0);
//	__context.emplace(move(representation), __context.size());
//}

} /* namespace xreate */
