/* 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/.
 *
 * File:   targetinterpretation.cpp
 * Author: pgess <v.melnychenko@xreate.org>
 *
 * Created on June 29, 2016, 6:45 PM
 */

/**
 * \file    targetinterpretation.h
 * \brief   Interpretation support. See more [details on Interpretation](/w/concepts/dsl/)
 */

#include "compilation/targetinterpretation.h"
#include "pass/interpretationpass.h"
#include "analysis/typeinference.h"
#include "llvmlayer.h"
#include "compilation/scopedecorators.h"
#include "compilation/interpretation-instructions.h"

#include <boost/scoped_ptr.hpp>
#include <iostream>
#include <csignal>

using namespace std;
using namespace xreate::compilation;

namespace xreate{
namespace interpretation{

const Expression EXPRESSION_FALSE = Expression(Atom<Number_t>(0));
const Expression EXPRESSION_TRUE = Expression(Atom<Number_t>(1));

CodeScope*
InterpretationScope::processOperatorIf(const Expression& expression) {
    const Expression& exprCondition = process(expression.getOperands()[0]);

    if (exprCondition == EXPRESSION_TRUE) {
        return expression.blocks.front();
    }

    return expression.blocks.back();
}

CodeScope*
InterpretationScope::processOperatorSwitch(const Expression& expression) {
    const Expression& exprCondition = process(expression.operands[0]);

    bool flagHasDefault = expression.operands[1].op == Operator::CASE_DEFAULT;

    //TODO check that one and only one case variant is appropriate
    for (size_t size = expression.operands.size(), i = flagHasDefault ? 2 : 1; i < size; ++i) {
        const Expression& exprCase = process(expression.operands[i]);

        if (function->getScope((const CodeScope*) exprCase.blocks.front())->processScope() == exprCondition) {
            return exprCase.blocks.back();
        }
    }

    if (flagHasDefault) {
        const Expression& exprCaseDefault = expression.operands[1];
        return exprCaseDefault.blocks.front();
    }

    assert(false && "Switch has no appropriate variant");
    return nullptr;
}

CodeScope*
InterpretationScope::processOperatorSwitchVariant(const Expression& expression) {
    const Expression& condition = process(expression.operands.at(0));
    assert(condition.op == Operator::VARIANT);
    const string& identCondition = expression.bindings.front();

    Expression opExpected(Atom<Number_t>(condition.getValueDouble()));
    auto itFoundValue = std::find(++expression.operands.begin(), expression.operands.end(), opExpected);
    assert(itFoundValue != expression.operands.end());

    int indexBlock = itFoundValue - expression.operands.begin() - 1;
    auto blockFound = expression.blocks.begin();
    std::advance(blockFound, indexBlock);

    InterpretationScope* scopeI12n = function->getScope(*blockFound);

    if(condition.operands.size()) {
        const Expression& value = condition.operands.at(0);
        scopeI12n->overrideBindings({
            {value, identCondition}
        });
    }

    return *blockFound;
}

llvm::Value*
InterpretationScope::processLate(const InterpretationOperator& op, const Expression& expression, const Context& context) {
    switch(op) {
    case IF_INTERPRET_CONDITION:
    {
        CodeScope* scopeResult = processOperatorIf(expression);

        llvm::Value* result = context.function->getScopeUnit(scopeResult)->compile();
        return result;
    }

    case SWITCH_INTERPRET_CONDITION:
    {
        CodeScope* scopeResult = processOperatorSwitch(expression);

        llvm::Value* result = context.function->getScopeUnit(scopeResult)->compile();
        return result;
    }

    case SWITCH_VARIANT:
    {
        CodeScope* scopeResult = processOperatorSwitchVariant(expression);
        const Expression& condition = expression.operands.at(0);
        const Expression& valueCondition = process(condition);

        const string identCondition = expression.bindings.front();
        auto scopeCompilation = Decorators<CachedScopeDecoratorTag>::getInterface(context.function->getScopeUnit(scopeResult));

        if(valueCondition.operands.size()) {
            //override value
            Symbol symbCondition{ScopedSymbol{scopeResult->__identifiers.at(identCondition), versions::VERSION_NONE}, scopeResult};
            scopeCompilation->overrideDeclarations({
                {symbCondition, Expression(valueCondition.operands.at(0))}}
            );

            //set correct type for binding:
            TypeAnnotation typeVariant = typeinference::getType(condition, *function->man->ast);
            int conditionIndex = valueCondition.getValueDouble();
            ScopedSymbol symbolInternal = scopeResult->getSymbol(identCondition);
            scopeResult->__declarations[symbolInternal].bindType(typeVariant.__operands.at(conditionIndex));
        }

        llvm::Value* result = context.function->getScopeUnit(scopeResult)->compile();
        return result;
    }

    case SWITCH_LATE:
    {
        latereasoning::LateReasoningCompiler compiler(dynamic_cast<InterpretationFunction*>(this->function), context);
        return compiler.processSwitchLateStatement(expression, "");
    }

    case FOLD_INTERPRET_INPUT:
    {
        //initialization
        const Expression& exprInput = process(expression.getOperands()[0]);
        assert(exprInput.op == Operator::LIST);

        CodeScope* scopeBody = expression.blocks.front();

        const string& nameEl = expression.bindings[0];
        Symbol symbEl{ScopedSymbol{scopeBody->__identifiers.at(nameEl), versions::VERSION_NONE}, scopeBody};
        const std::string& idAccum = expression.bindings[1];
        llvm::Value* rawAccum = context.scope->process(expression.getOperands()[1]);

        InterpretationScope* intrBody = function->getScope(scopeBody);
        auto unitBody = Decorators<CachedScopeDecoratorTag>::getInterface(context.function->getScopeUnit(scopeBody));

        const std::vector<Expression> elementsInput = exprInput.getOperands();

        for(size_t i = 0; i < elementsInput.size(); ++i) {
            const Expression& exprElement = elementsInput[i];

            intrBody->overrideBindings({
                {exprElement, nameEl}
            });
            unitBody->overrideDeclarations({
                {symbEl, exprElement}
            }); //resets unitBody
            unitBody->bindArg(rawAccum, string(idAccum));

            rawAccum = unitBody->compile();
        }

        return rawAccum;
    }

//    case FOLD_INF_INTERPRET_INOUT:
//    {
//    }

        //TODO refactor as InterpretationCallStatement class
    case CALL_INTERPRET_PARTIAL:
    {
        const std::string &calleeName = expression.getValueString();
        ICodeScopeUnit* scopeUnitSelf = context.scope;
        ManagedFnPtr callee = this->function->man->ast->findFunction(calleeName);
        const  FunctionInterpretationData& calleeData = FunctionInterpretationHelper::getSignature(callee);
        std::vector<llvm::Value *> argsActual;
        PIFSignature sig;
        sig.declaration = callee;

        for(size_t no = 0, size = expression.operands.size(); no < size; ++no) {
            const Expression& op =  expression.operands[no];

            if (calleeData.signature.at(no) == INTR_ONLY) {
                sig.bindings.push_back(process(op));
                continue;
            }

            argsActual.push_back(scopeUnitSelf->process(op));
        }

        TargetInterpretation* man = dynamic_cast<TargetInterpretation*> (this->function->man);
        PIFunction* pifunction =  man->getFunction(move(sig));

        llvm::Function* raw = pifunction->compile();
        boost::scoped_ptr<BruteFnInvocation> statement(new BruteFnInvocation(raw, man->pass->man->llvm));
        return (*statement)(move(argsActual));
    }
    
    case QUERY_LATE:
    {
        return IntrinsicQueryInstruction(
            dynamic_cast<InterpretationFunction*>(this->function))
            .processLate(expression, context);
    }

    default: break;
    }

    assert(false && "Unknown late interpretation operator");
    return nullptr;
}

llvm::Value*
InterpretationScope::compile(const Expression& expression, const Context& context) {
    const InterpretationData& data = Attachments::get<InterpretationData>(expression);

    if (data.op != InterpretationOperator::NONE) {
        return processLate(data.op, expression, context);
    }

    Expression result = process(expression);
    return context.scope->process(result);
}

Expression
InterpretationScope::process(const Expression& expression) {
#ifndef NDEBUG
    if (expression.tags.count("bpoint")) {
        std::raise(SIGINT);
    }
#endif

    PassManager* man = (static_cast<TargetInterpretation*> (function->man))->pass->man;

    switch (expression.__state) {
    case Expression::INVALID:
        assert(false);

    case Expression::NUMBER:
    case Expression::STRING:
        return expression;

    case Expression::IDENT:
    {
        Symbol s = Attachments::get<IdentifierSymbol>(expression);
        return Parent::processSymbol(s);
    }

    case Expression::COMPOUND:
        break;

    default: assert(false);
    }

    switch (expression.op) {
    case Operator::EQU:
    {
        const Expression& left = process(expression.operands[0]);
        const Expression& right = process(expression.operands[1]);

        if (left == right) return EXPRESSION_TRUE;
        return EXPRESSION_FALSE;
    }

    case Operator::NE:
    {
        const Expression& left = process(expression.operands[0]);
        const Expression& right = process(expression.operands[1]);

        if (left == right) return EXPRESSION_FALSE;
        return EXPRESSION_TRUE;
    }

    case Operator::LOGIC_AND:
    {
        assert(expression.operands.size() == 1);
        return process (expression.operands[0]);
    }


        //        case Operator::LOGIC_OR:
    case Operator::CALL:
    {
        const std::string &fnName = expression.getValueString();
        ManagedFnPtr fnAst = this->function->man->ast->findFunction(fnName);
        InterpretationFunction* fnUnit = this->function->man->getFunction(fnAst);

        vector<Expression> args;
        args.reserve(expression.getOperands().size());

        for(size_t i = 0, size = expression.getOperands().size(); i < size; ++i) {
            args.push_back(process(expression.getOperands()[i]));
        }

        return fnUnit->process(args);
    }

    case Operator::CALL_INTRINSIC:
    {
        assert(false && "Unknown intrinsic");
    }

    case Operator::QUERY:
    {
        return IntrinsicQueryInstruction(dynamic_cast<InterpretationFunction*>(this->function))
            .process(expression);
    }
    
    case Operator::QUERY_LATE:
    {
        assert(false && "Can't be interpretated");
        return Expression();
    }

    case Operator::IF:
    {
        CodeScope* scopeResult = processOperatorIf(expression);
        return function->getScope(scopeResult)->processScope();
    }

    case Operator::SWITCH:
    {
        CodeScope* scopeResult = processOperatorSwitch(expression);
        return function->getScope(scopeResult)->processScope();
    }

    case Operator::SWITCH_VARIANT:
    {
        CodeScope* scopeResult = processOperatorSwitchVariant(expression);
        return function->getScope(scopeResult)->processScope();
    }

    case Operator::VARIANT:
    {
        if(!expression.operands.size()) return expression;

        Expression variantData = process(expression.operands[0]);
        Expression result{Operator::VARIANT, {variantData}};
        result.setValueDouble(expression.getValueDouble());
        return result;
    }

    case Operator::INDEX:
    {
        Expression exprData = process(expression.operands[0]);

        for (size_t keyId = 1; keyId < expression.operands.size(); ++keyId) {
            const Expression& exprKey = process(expression.operands[keyId]);

            if (exprKey.__state == Expression::STRING) {
                const string& key = exprKey.getValueString();
                assert(exprData.__indexBindings.count(key));
                size_t idxKey = exprData.__indexBindings.at(key);

                exprData = Expression(exprData.operands.at(idxKey));
                continue;
            }

            if (exprKey.__state == Expression::NUMBER) {
                int key = exprKey.getValueDouble();
                exprData = Expression(exprData.operands[key]);
                continue;
            }

            assert(false && "Inappropriate key");
        }

        return exprData;
    }

    case Operator::FOLD:
    {
        const Expression& exprInput = process(expression.getOperands()[0]);
        const Expression& exprInit = process(expression.getOperands()[1]);

        const std::string& argEl = expression.bindings[0];
        const std::string& argAccum = expression.bindings[1];

        InterpretationScope* body = function->getScope(expression.blocks.front());

        Expression accum = exprInit;
        for(size_t size = exprInput.getOperands().size(), i = 0; i < size; ++i) {
            body->overrideBindings({
                {exprInput.getOperands()[i], argEl},
                {accum, argAccum}
            });

            accum = body->processScope();
        }

        return accum;
    }

    case Operator::LIST:
    case Operator::LIST_RANGE:
    {
        Expression result(expression.op,{});
        result.operands.resize(expression.operands.size());
        result.bindings = expression.bindings;
        result.__indexBindings = expression.__indexBindings;

        int keyId = 0;
        for(const Expression& opCurrent : expression.operands) {
            result.operands[keyId++] = process(opCurrent);
        }

        return result;
    }

        //        case Operator::MAP: {
        //            break;
        //        }

    default: break;
    }

    return expression;
}

InterpretationFunction*
TargetInterpretation::getFunction(IFunctionUnit* unit) {
    if (__dictFunctionsByUnit.count(unit)) {
        return __dictFunctionsByUnit.at(unit);
    }

    InterpretationFunction* f = new InterpretationFunction(unit->function, this);
    __dictFunctionsByUnit.emplace(unit, f);
    assert(__functions.emplace(unit->function.id(), f).second);

    return f;
}

PIFunction*
TargetInterpretation::getFunction(PIFSignature&& sig) {
    auto f = __pifunctions.find(sig);
    if (f != __pifunctions.end()) {
        return f->second;
    }

    PIFunction* result  = new PIFunction(PIFSignature(sig), __pifunctions.size(), this);
    __pifunctions.emplace(move(sig), result);
    assert(__dictFunctionsByUnit.emplace(result->functionUnit, result).second);

    return result;
}

InterpretationScope*
TargetInterpretation::transformContext(const Context& c) {
    return this->getFunction(c.function)->getScope(c.scope->scope);
}

llvm::Value*
TargetInterpretation::compile(const Expression& expression, const Context& ctx) {
    return transformContext(ctx)->compile(expression, ctx);
}

InterpretationFunction::InterpretationFunction(const ManagedFnPtr& function, Target<TargetInterpretation>* target)
: Function<TargetInterpretation>(function, target) { }

Expression
InterpretationFunction::process(const std::vector<Expression>& args) {
    InterpretationScope* body = getScope(__function->__entry);

    list<pair<Expression, string>> bindings;
    for(size_t i = 0, size = args.size(); i < size; ++i) {
        bindings.push_back(make_pair(args.at(i), body->scope->__bindings.at(i)));
    }

    body->overrideBindings(bindings);
    return body->processScope();
}

//                  Partial function interpretation

typedef BasicFunctionUnit PIFunctionUnitParent;

class PIFunctionUnit : public PIFunctionUnitParent{
public:

    PIFunctionUnit(ManagedFnPtr f, std::set<size_t>&& arguments, size_t id, CompilePass* p)
    : PIFunctionUnitParent(f, p), argumentsActual(move(arguments)), __id(id) { }

protected:

    std::vector<llvm::Type*>
    prepareSignature() override {
        LLVMLayer* llvm = PIFunctionUnitParent::pass->man->llvm;
        AST* ast = PIFunctionUnitParent::pass->man->root;
        CodeScope* entry = PIFunctionUnitParent::function->__entry;
        std::vector<llvm::Type*> signature;

        for(size_t no : argumentsActual) {
            VNameId argId = entry->__identifiers.at(entry->__bindings.at(no));
            ScopedSymbol arg{argId, versions::VERSION_NONE};

            signature.push_back(llvm->toLLVMType(ast->expandType(entry->__declarations.at(arg).type)));
        }

        return signature;
    }

    llvm::Function::arg_iterator
    prepareBindings() override{
        CodeScope* entry = PIFunctionUnitParent::function->__entry;
        ICodeScopeUnit* entryCompilation = PIFunctionUnitParent::getScopeUnit(entry);
        llvm::Function::arg_iterator fargsI = PIFunctionUnitParent::raw->arg_begin();

        for(size_t no : argumentsActual) {
            ScopedSymbol arg{entry->__identifiers.at(entry->__bindings.at(no)), versions::VERSION_NONE};

            entryCompilation->bindArg(&*fargsI, arg);
            fargsI->setName(entry->__bindings.at(no));
            ++fargsI;
        }

        return fargsI;
    }

    virtual std::string
    prepareName() override {
        return PIFunctionUnitParent::prepareName() + "_" + std::to_string(__id);
    }

private:
    std::set<size_t> argumentsActual;
    size_t __id;
} ;

PIFunction::PIFunction(PIFSignature&& sig, size_t id, TargetInterpretation* target)
: InterpretationFunction(sig.declaration, target), signatureInstance(move(sig)) {
    const FunctionInterpretationData&  functionData = FunctionInterpretationHelper::getSignature(signatureInstance.declaration);

    std::set<size_t> argumentsActual;
    for (size_t no = 0, size = functionData.signature.size(); no < size; ++no) {
        if (functionData.signature.at(no) != INTR_ONLY) {
            argumentsActual.insert(no);
        }
    }

    functionUnit = new PIFunctionUnit(signatureInstance.declaration, move(argumentsActual), id, target->pass);
    CodeScope* entry = signatureInstance.declaration->__entry;
    auto entryUnit = Decorators<CachedScopeDecoratorTag>::getInterface<>(functionUnit->getEntry());
    InterpretationScope* entryIntrp = InterpretationFunction::getScope(entry);

    list<pair<Expression, std::string>> bindingsPartial;
    list<pair<Symbol, Expression>> declsPartial;

    for(size_t no = 0, sigNo = 0, size = entry->__bindings.size(); no < size; ++no) {
        if(functionData.signature.at(no) == INTR_ONLY) {
            bindingsPartial.push_back({signatureInstance.bindings[sigNo], entry->__bindings[no]});

            VNameId argId = entry->__identifiers.at(entry->__bindings[no]);
            Symbol argSymbol{ScopedSymbol
                {argId, versions::VERSION_NONE}, entry};
            declsPartial.push_back({argSymbol, signatureInstance.bindings[sigNo]});
            ++sigNo;
        }
    }

    entryIntrp->overrideBindings(bindingsPartial);
    entryUnit->overrideDeclarations(declsPartial);
}

llvm::Function*
PIFunction::compile() {
    llvm::Function* raw = functionUnit->compile();

    return raw;
}

bool operator<(const PIFSignature& lhs, const PIFSignature& rhs) {
    if (lhs.declaration.id() != rhs.declaration.id()) {
        return lhs.declaration.id() < rhs.declaration.id();
    }

    return lhs.bindings < rhs.bindings;
}

bool operator<(const PIFSignature& lhs, PIFunction * const rhs) {
    return lhs < rhs->signatureInstance;
}

bool operator<(PIFunction * const lhs, const PIFSignature& rhs) {
    return lhs->signatureInstance < rhs;
}

}
}

/** \class xreate::interpretation::InterpretationFunction
 *
 * Holds list of xreate::interpretation::InterpretationScope 's focused on interpretation of individual code scopes
 *
 * There is particulat subclass  PIFunction intended to represent partially interpreted functions
 *\sa TargetInterpretation, [Interpretation Concept](/w/concepts/dfa)
 */
/** \class xreate::interpretation::TargetInterpretation
 *
 * Executed during compilation and intented to preprocess eligible parts of code.
 * Established on [Targets Infrastructure](\ref compilation::Target)
 *
 * Holds list of InterpretationFunction / PIFunction to represent interpretation process for individual functions
 *
 * In order to be activated during compilation process there is
 *  InterpretationScopeDecorator implementation of ICodeScopeUnit
 * \sa InterpretationPass, compilation::Target, [Interpretation Concept](/w/concepts/dfa)
 *
 */
