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

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

using namespace std;
namespace xreate {

ContextQuery::ContextQuery()
	: domainEmpty(vector<Expression>()) {}

const ContextDomain&
ContextQuery::getContext(const ScopePacked& scopeId){
	if (!__modelContext.count(scopeId)){
		return domainEmpty;
	}

	return __modelContext.at(scopeId);
}

const ContextDomain&
ContextQuery::getContext(CodeScope* const scope){
	return getContext(clasp->pack(scope));
}

void
ContextQuery::forceContext(const ScopePacked& scopeId, std::list<Expression> context){
	// TODO remove forced context of the same class/type only, possibly
		//remove any previous forced context for this scopeId
	//__modelForcedContext.erase(scopeId);

	//TASK restore forceContext
		//std::multimap<ScopePacked, Expression> __modelForcedContext;
		/*
	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");

	this->clasp = clasp;
	ClaspLayer::ModelFragment query = clasp->query(atomEarlyBinding);

		//static context
	if (query){
		map<ScopePacked, vector<Expression>> dictContext;

		for (auto i = query->first; i!=query->second; ++i){
			ScopePacked idScope;
			Expression context;
			tie(idScope, context) = ClaspLayer::parse<ScopePacked, Expression>(i->second);
			dictContext[idScope].push_back(context);
		}

		for (map<ScopePacked, vector<Expression>>::value_type& entry: dictContext){
			__modelContext.insert(move(entry));
		}
	}

	//static decisions
	query = clasp->query(Config::get("clasp.bindings.scope_decision"));
	if (query){
		for (auto i = query->first; i!=query->second; ++i){
			ScopePacked scopeId;
			string function;
			Expression specialization;
			tie(scopeId, function, specialization) = ClaspLayer::parse<ScopePacked, string, Expression>(i->second);
			ScopeContextDecisions& scope = __modelStaticDecisions[scopeId];
			assert(scope.emplace(function, specialization).second && "Possibly more than one decision");
		}
	}

	//Fetch function specialization domains
	std::multimap<string, Expression> modelDomains;
	const list<ManagedFnPtr>& functions = clasp->ast->getAllFunctions();
	for (auto f: functions){
		if (f->guardContext.isValid()){
			modelDomains.emplace(f->getName(), f->guardContext);
		}
	}

	auto adapter = [](const std::pair<const string&,const Expression>& p){ return p.second; };

	auto iBegin = modelDomains.begin();
	while (iBegin!=modelDomains.end()){
		string function = iBegin->first;
		auto iEnd = modelDomains.upper_bound(function);
		auto iBeginAdapted = boost::make_transform_iterator(iBegin,adapter);
		auto iEndAdapted = boost::make_transform_iterator(iEnd,adapter);
		ContextDomain dom(iBeginAdapted, iEndAdapted);
		__modelFunctionDomain.emplace(function, move(dom));
		iBegin = iEnd;
	}

	prepareDemandModel();
}

const ContextDomain&
ContextQuery::getFunctionDomain(const std::string& name) const {
	if (__modelFunctionDomain.count(name)){
		return __modelFunctionDomain.at(name);
	}

	return domainEmpty;
}

const FunctionContextDemand&
ContextQuery::getFunctionDemand(const std::string& name) const {
	if (__modelFunctionDemand.count(name)){
		return __modelFunctionDemand.at(name);
	}

	return decisionsEmpty;
}

const ScopeContextDecisions&
ContextQuery::getStaticDecisions(const ScopePacked& scopeId){
	if (__modelStaticDecisions.count(scopeId)){
		return __modelStaticDecisions[scopeId];
	}

	return contextDecisionsEmpty;
}

void
ContextQuery::prepareDemandModel(){
	const std::string& atomFunctionDemand = Config::get("clasp.bindings.function_demand");

	ClaspLayer::ModelFragment query = clasp->query(atomFunctionDemand);
	if (query)
	for (auto i = query->first; i!=query->second; ++i){
		string function;
		string decision;
		tie(function, decision) = ClaspLayer::parse<string, string>(i->second);

		FunctionContextDemand& decisions = __modelFunctionDemand[function];
		decisions.left.insert(make_pair(decisions.size(), decision));
	}
}

//	const std::string& atomLateBinding = Config::get("clasp.bindings.function_uncertain");
//	query = clasp->query(atomLateBinding);
//
//	std::map<std::string, std::vector<Expression>> dictFunctionDomain;
//	if (query){
//		for (auto i = query->first; i!=query->second; ++i){
//			string nameFunction;
//			Expression context;
//			tie(nameFunction, context) = ClaspLayer::parse<ScopePacked, Expression>(i->second);
//			dictFunctionDomain.at(nameFunction).push_back(context);
//		}
//
//		for(auto& entry: dictFunctionDomain){
//			__modelFunctionDomain.emplace(entry.first, move(entry.second));
//		}
//	}

} /* namespace xreate */
