/*
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * Author: pgess <v.melnychenko@xreate.org>
 * Created on June 15, 2018, 5:32 PM
 */

#include "compilation/i12ninst.h"
#include "analysis/interpretation.h"
#include "transcendlayer.h"
#include "targets.h"
#include "aux/latereasoning.h"
#include "latetranscend.h"
#include "aux/transcend-decorators.h"
#include "compilation/targetinterpretation.h"

using namespace std;
using namespace xreate::latereasoning;

namespace xreate{
namespace interpretation{

Expression
IntrinsicQueryInstruction::process(const Expression& expression) {
    AST* ast = static_cast<TargetInterpretation*> (__fnI12n->man)->ast;
    TranscendLayer* transcend = static_cast<TargetInterpretation*> (__fnI12n->man)->pass->man->transcend;

    ExpandedType targetT = ast->getType(expression);
    assert(expression.operands.size() == 1);
    assert(expression.operands.front().__state == Expression::STRING);
    assert(targetT->__operator == TypeOperator::LIST_ARRAY);

    std::string namePredicate = expression.operands.front().getValueString();
    StaticModel model = (static_cast<TargetInterpretation*> (__fnI12n->man))->pass->man->transcend->query(namePredicate);

    Expression result(Operator::LIST,{});
    result.operands.reserve(model.size());

    ExpandedType elementT = targetT->__operands.at(0).__operator == TypeOperator::SLAVE
        ? dereferenceSlaveType(ExpandedType(targetT->__operands.at(0)), transcend)
        : ExpandedType(targetT->__operands.at(0));

    if(model.size()) {
        if (elementT->__operator == TypeOperator::LIST_RECORD) {
            //edge case, content's type is LIST_NAMED:
            for(const auto& row : model) {
                result.operands.push_back(representTransExpression(row.second, elementT, transcend));
            }
        } else {
            for (const auto& row : model) {
                assert(row.second.args().size);
                result.operands.push_back(representTransExpression(row.second.args()[0], elementT, transcend));
            }
        }
    }

    return result;
}

llvm::Value*
IntrinsicQueryInstruction::processLate(const Expression& expression, const compilation::Context& context) {
    assert(expression.blocks.size());
    CodeScope* body = expression.blocks.front();
    PassManager* man = static_cast<TargetInterpretation*> (__fnI12n->man)->pass->man;
    compilation::ICodeScopeUnit* bodyBrute = context.function->getScopeUnit(body);
    
    llvm::Type* instructionT = man->llvm->toLLVMType(man->root->getType(expression));
    Expression atomNameE = __fnI12n->getScope(context.scope->scope)->process(expression.operands.front());
    assert(atomNameE.__state == Expression::STRING);
    string atomName = atomNameE.getValueString();
    std::string argName = expression.bindings.front();
    ExpandedType argT = man->root->getType(body->getDefinition(body->getSymbol(argName)));
    if (argT->__operator == TypeOperator::SLAVE){
        argT = dereferenceSlaveType(argT, man->transcend);
    }
    
    auto transcend =
        Decorators<LateReasoningTranscendDecoratorTag>::getInterface(man->transcend);
    LateReasoningCompiler* compiler =
        new latereasoning::LateReasoningCompiler(__fnI12n, context);
    SymbolPacked targetSP = transcend->pack(Attachments::get<ExprAlias_A>(expression));

    LateAnnotationsGroup feedbackAG = transcend->queryLate(atomName);
    for(const auto& feedback : feedbackAG.annotations) {
        SymbolPacked targetExpectedSP = ParseImplAtom<SymbolPacked>::get(feedback.first);
        if (targetExpectedSP == targetSP) {
            const LateAnnotation& annotation = feedback.second;
            return compiler->compileAutoExpand(annotation, instructionT, "",
                [transcend, &argT, this, body, bodyBrute, &argName](const Gringo::Symbol & atomRaw) {
                    InterpretationScope* bodyI12n = __fnI12n->getScope(body);
                    Expression argValue;
                    if (argT->__operator == TypeOperator::LIST_RECORD) {
                        argValue = representTransExpression(atomRaw, argT, transcend);
                    } else {
                        argValue = representTransExpression(atomRaw.args()[0], argT, transcend);
                    }
                    
                    bodyI12n->overrideBindings({
                        {argValue, argName}});
                    bodyBrute->reset();
                    return bodyBrute->compile();
                });
        }
    }

    assert(false && "No appropriate late annotation");
    return nullptr; //should not be ever reachable
}

}
} //end of xreate::interpretation
