/*
 * logging.cpp
 *
 *  Created on: Jun 23, 2015
 *      Author: pgess
 */

#include <pass/loggerpass.h>
#include "compilation/instr-containers.h"
#include "utils.h"

using namespace std;
using namespace llvm;
namespace xreate {


void LoggerPass::init(ClaspLayer* clasp){
	auto model = clasp->query(Config::get("logging.id"));
	if(!model) return;

	for (ClaspLayer::ModelIterator rec = model->first; rec!=model->second; ++rec){
		std::tuple<SymbolPacked> _v =ClaspLayer::parse<SymbolPacked>(rec->second);
		Symbol v = clasp->unpack(get<0>(_v));

		SymbolAttachments::put<IsLogging>(v, true);
	}
}

void
LoggerPass::process(const Expression& expression, PassContext context, const std::string& varDecl){
	if (varDecl.size()){
		Symbol v = context.scope->findSymbol(varDecl);
		if (SymbolAttachments::get<IsLogging>(v, false)){
				CompilePass::FunctionUnit* func =  compiler->getFunctionUnit(context.function);
				CompilePass::CodeScopeUnit* scope = func->getScopeUnit(context.scope);

				CompilePass::Context compilationContext{func, scope, compiler};
				inject(v, compilationContext);
		}
	}

	return AbstractPass<void>::process(expression, context, varDecl);
}

void LoggerPass::inject(const Symbol& symbol, const CompilePass::Context& context){
	llvm::Value* source = context.scope->compileSymbol(symbol);
	ExpandedType typSource = man->root->expandType(CodeScope::findDefinition(symbol));
	string format = "";
	switch (typSource->__value) {
	case TypePrimitive::Int : case TypePrimitive::Num : case TypePrimitive::I32: case TypePrimitive::I8:
		format = "%d\n";
		break;

	case TypePrimitive::String:
		format = "%s\n";
		break;

	default:
		assert(false && "No appropriate type for logging");
	}

	containers::Instructions compiler(context);

	LLVMLayer* llvm = context.pass->man->llvm;
	llvm->builder.SetInsertPoint(*source->use_begin());
	llvm::Value* formatRaw = compiler.compileConstantStringAsPChar(format, "logformat");

	llvm->builder.CreateCall2(refPrintf, formatRaw, source);
}

void
LoggerPass::initOutput(){
	LLVMLayer* llvm = man->llvm;
	refPrintf = llvm->module->getFunction("printf");

	 if (!refPrintf) {
		 PointerType* typPtrI8 = PointerType::get(IntegerType::get(llvm->module->getContext(), 8), 0);
		 std::vector<Type*>argsPrintf{typPtrI8};
		 FunctionType* signaturePrintf = FunctionType::get(
		  /*Result=*/IntegerType::get(llvm->module->getContext(), 32),
		  /*Params=*/argsPrintf,
		  /*isVarArg=*/true);

		 refPrintf = llvm::Function::Create(
	  /*Type=*/signaturePrintf,
	  /*Linkage=*/GlobalValue::ExternalLinkage,
	  /*Name=*/"printf", llvm->module); // (external, no body)

		 refPrintf->setCallingConv(CallingConv::C);
	 }
}

LoggerPass::LoggerPass(PassManager* manager)
	: AbstractPass<void>(manager)
{
	initOutput();
	init(man->clasp);
}

void
LoggerPass::initDependencies(CompilePass* pass){
	compiler = pass;
}


} /* namespace xreate */
