/*
 * adhoc.cpp
 *
 *  Created on: Nov 28, 2015
 *      Author: pgess
 */

#include "pass/adhocpass.h"
#include "query/context.h"

namespace xreate {

AdhocExpression::AdhocExpression(): Expression(Operator::ADHOC, {})
{}

AdhocExpression::AdhocExpression(const Expression& base): Expression(base)
{}

void
AdhocExpression::setCommand(const Expression& comm){
    this->addTags({Expression(Operator::CALL, {Atom<Identifier_t>("adhoc"), comm})});
}

Expression
AdhocExpression::getCommand() const{
    assert(this->tags.count("adhoc"));
    return this->tags.at("adhoc").getOperands().at(0);
}

AdhocScheme*
AdhocPass::findAssotiatedScheme(CodeScope* entry){
    const ScopePacked scopeId = man->clasp->pack(entry);
    const Domain& domain = queryContext->getContext(scopeId);
    AdhocScheme* scheme = nullptr;

    for (const Expression& context: domain){
            if (context.__state != Expression::IDENT) continue;

            if (__schemes.count(context.getValueString())){
                    assert(!scheme && "Can't determine relevant scheme, ambiguous context");
                    scheme = __schemes.at(context.getValueString());
            }
    }

    assert(scheme && "Context doesn't define any ad hoc scheme");
    return scheme;
}

const TypeAnnotation&
AdhocScheme::getResultType(){
    return __resultType;
}

CodeScope*
AdhocScheme::getCommandImplementation(const Expression& comm) {
    assert(comm.__state == Expression::IDENT);

    const std::string commSerialized =  comm.getValueString();
    assert(__commands.count(commSerialized) && "Command isn't defined for a selected scheme");
    return __commands.at(commSerialized);
}

AdhocScheme::AdhocScheme(const Expression& scheme):
    __resultType(scheme.type), __name(scheme.getValueString()) {

    Expression exprCasesList = scheme.getOperands()[0];
    for (const Expression& exprSingleCase: exprCasesList.getOperands()){
        std::string command = exprSingleCase.tags.begin()->second.getValueString();

        CodeScope* blockImpl = *(exprSingleCase.blocks.begin());
        __commands.emplace(command, blockImpl);
    }
}

const std::string&
AdhocScheme::getName(){
    return __name;
}

void
AdhocPass::run(){
    queryContext = reinterpret_cast<ContextQuery*>(man->clasp->registerQuery(new ContextQuery(), QueryId::ContextQuery));

    auto range = man->root->__interfacesData.equal_range(ASTInterface::Adhoc);
    for (auto i=range.first; i!= range.second; ++i){
        AdhocScheme* scheme = new AdhocScheme(i->second);
        __schemes.emplace(scheme->getName(), scheme);
    }
}

}




