/*
 * adhoc.cpp
 *
 *  Created on: Dec 1, 2015
 *      Author: pgess
 */

#include <query/context.h>
#include <boost/range/iterator_range_core.hpp>

using namespace std;
namespace xreate {

std::list<Expression>
ContextQuery::getContext(const ScopePacked& scopeId){
	std::list<Expression> result;

	for (auto i: boost::make_iterator_range(__modelContext.equal_range(scopeId))){
		result.push_back(i.second);
	}

	for (auto i: boost::make_iterator_range(__modelForcedContext.equal_range(scopeId))){
			result.push_back(i.second);
	}

	return result;
}

void
ContextQuery::forceContext(const ScopePacked& scopeId, std::list<Expression> context){
		// TODO remove forced context only of the same class, possibly

		//remove any previous forced context for this scopeId
	__modelForcedContext.erase(scopeId);

	std::transform(context.begin(), context.end(), inserter(__modelForcedContext, __modelForcedContext.end()),
			[scopeId](const Expression& context){return make_pair(scopeId, context);});
}

void
ContextQuery::init(ClaspLayer* clasp){
	const std::string& atomEarlyBinding = Config::get("clasp.bindings.scope");
	ClaspLayer::ModelFragment model = clasp->query(atomEarlyBinding);

	if (model){
		for (auto i = model->first; i!=model->second; ++i){
			ScopePacked idScope;
			Expression context;
			tie(idScope, context) = ClaspLayer::parse<ScopePacked, Expression>(i->second);
			__modelContext.emplace(idScope, context);
		}
	}

	const std::string& atomLateBinding = Config::get("clasp.bindings.scope_weak");
	model = clasp->query(atomEarlyBinding);

	std::map<std::string, std::vector<Expression>> modelLateContext;
	if (model){
		for (auto i = model->first; i!=model->second; ++i){
			string nameFunction;
			Expression context;
			tie(nameFunction, context) = ClaspLayer::parse<ScopePacked, Expression>(i->second);
			modelLateContext.at(nameFunction).push_back(context);
		}

		for(auto& entry: modelLateContext){
			__modelFunctionContext.emplace(entry.first, move(entry.second));
		}
	}
}

const ContextDomain&
ContextQuery::getFunctionDomain(const std::string& name) const {
	static ContextDomain domainNULL({});

	if (__modelFunctionContext.count(name)){
		return __modelFunctionContext.at(name);
	}

	return domainNULL;
}

ContextDomain::ContextDomain(const std::vector<Expression>&& expressions)
	: std::vector<Expression>(move(expressions)){
	for (const Expression& e: expressions){
			serializer.registerExpression(e);
	}

	size_t id =0;
	for (const Expression& e: expressions){
			scheme.emplace(serializer.pack(e), id++);
	}
}

size_t
ContextDomain::size() const{
	return  PARENT::size();
}


ContextDomain::const_iterator
ContextDomain::begin() const {
	return begin();
}

ContextDomain::const_iterator
ContextDomain::end() const {
	return end();
}

boost::optional<size_t>
ContextDomain::getExpressionId(const Expression& e) const{
	const auto& exprPacked = serializer.packOptional(e);
	if (!exprPacked){
		return boost::none;
	}

	if (scheme.count(*exprPacked)){
		return scheme.at(*exprPacked);
	}

	return boost::none;
}

const Expression&
ContextDomain::get(size_t id) const{
	return at(id);
}
} /* namespace xreate */
