#include <kimproxy.h>
#include "dfgpass.h"
#include "../passmanager.h"

using namespace xreate;
using namespace std;

DFGPass::DFGPass(PassManager* manager)
        : AbstractPass(manager)
{}



SymbolPacked
DFGPass::processExpression(const Expression& expression, PassContext context, const std::string& decl)
{
    switch(expression.op) {
        case Operator::CALL: {
            const string &name = expression.getValueString();
            ManagedFnPtr function = man->root->findFunction(name);
            CodeScope *scopeRemote = function->getEntryScope();

            std::vector<SymbolPacked> operands;
            operands.reserve(expression.getOperands().size());
            for (const Expression &op: expression.getOperands()) {
                operands.push_back(processExpression(op, context));
            }

            std::vector<SymbolPacked>::iterator op = operands.begin();
            for (const std::string &arg: scopeRemote->__args) {
                if (op->isValid()) {
                    const Symbol &nodeRemote = scopeRemote->findSymbol(arg, *man->llvm, false);
                    __context.graph.addLink(__context.graph.pack(nodeRemote), *op, DFGConnection::OPT);
                }

                ++op;
            }

            SymbolPacked ret = __context.graph.pack(Symbol{0, scopeRemote});
            if (!decl.empty()) {
                __context.graph.addLink(
                        __context.graph.pack(
                                context.scope->findSymbol(decl, *man->llvm, false)),
                        ret, DFGConnection::OPT);
            }

            return ret;
        }

        default: {
            std::vector<SymbolPacked> operands;

            if (__schemes.count(expression.op)) {
                const Expression &scheme = __schemes.at(expression.op);

                operands.reserve(expression.getOperands().size());
                for (const Expression &op: expression.getOperands()) {
                    operands.push_back(processExpression(op, context));
                }

                std::vector<SymbolPacked>::const_iterator arg = operands.begin();
                std::vector<Expression>::const_iterator tag = ++scheme.getOperands().begin();

                while (tag != scheme.getOperands().end()) {
                    if (arg->isValid() && tag->__state != Expression::INVALID) {
                        __context.graph.addTag(*arg, Expression(*tag));
                    }

                    ++arg; ++tag;
                }

                Expression retTag = *scheme.getOperands().begin();
                if (retTag.__state != Expression::INVALID) {
                    assert(!decl.empty());
                    SymbolPacked pdecl = __context.graph.pack(context.scope->findSymbol(decl, *man->llvm, false));
                    __context.graph.addTag(pdecl, move(retTag));
                }
            }

                    // adhoc for MAP case, TODO reorganize code in more clear manner
            if (expression.op == Operator::MAP) {
                SymbolPacked nodeFrom;

                if (operands.size()) {
                    nodeFrom = operands.at(0);
                } else {
                    nodeFrom = processExpression(expression.getOperands().at(0), context);
                }

                assert(!decl.empty());
                SymbolPacked nodeTo = __context.graph.pack(context.scope->findSymbol(decl, *man->llvm, false));
                __context.graph.addLink(move(nodeTo), move(nodeFrom), DFGConnection::PROTO);
            }
        }

    }

    switch(expression.__state) {
        case Expression::IDENT:
            AbstractPass::process(expression, context, decl);

            string ident = expression.getValueString();
            const Symbol& identSymbol =  context.scope->findSymbol(ident, *man->llvm);

            SymbolPacked nodeFrom = __context.graph.pack(identSymbol);
            if (!decl.empty()) {
                SymbolPacked nodeTo = __context.graph.pack(context.scope->findSymbol(decl, *man->llvm));
                __context.graph.addLink(move(nodeTo), move(nodeFrom), DFGConnection::STRONG);
            }

            return nodeFrom;
    }

    AbstractPass::process(expression, context, decl);
    return SYMBOL_INVALID;
}

void
DFGPass::run()
{
    init();
    return AbstractPass::run();
}

void
DFGPass::process(const Expression& expression, PassContext context, const std::string& decl)
{
    processExpression(expression, context, decl);
}


void
DFGPass::init()
{
    for (const Expression& scheme: man->root->__dfadata)
    {
        __schemes.emplace(scheme.op, scheme);
    }
}

void DFGPass::finish()
{
    man->clasp->addDFAData(move(__context.graph));
}