#include "dfgpass.h"
#include "../passmanager.h"

using namespace xreate;
using namespace std;

DFGPass::DFGPass(PassManager* manager)
        : AbstractPass(manager), clasp(man->clasp)
{}


SymbolPackedOptional
DFGPass::process(const Expression& expression, PassContext context, const std::string& decl)
{
        // write down adhoc expression tags:
    if (expression.tags.size() && decl.length()) {
        for (pair<std::string, Expression> tag: expression.tags) {
            SymbolPacked nodeThis = clasp->pack(context.scope->findSymbol(decl), decl);
            __context.graph.addTag(nodeThis, Expression(tag.second));
        }
    }

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

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

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

            return nodeFrom;
    }

    switch(expression.op) {
        case Operator::CALL: {
            const string &name = expression.getValueString();


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

            ManagedFnPtr function = man->root->findFunction(name);
            if (!function) return boost::none;

            	// set calling relations:
            CodeScope *scopeRemote = function->getEntryScope();
            std::vector<SymbolPackedOptional>::iterator op = operands.begin();
            for (const std::string &arg: scopeRemote->__args) {
                if (*op) {
                    const Symbol &nodeRemote = scopeRemote->findSymbol(arg);
                    __context.graph.addLink(clasp->pack(nodeRemote), **op, DFGConnection::OPT);
                }

                ++op;
            }

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

            return ret;
        }
    }

	std::vector<SymbolPackedOptional> operands;

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

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

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

		while (tag != scheme.getOperands().end()) {
			if (*arg && 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 = clasp->pack(context.scope->findSymbol(decl));
			__context.graph.addTag(pdecl, move(retTag));
		}
	}

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

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

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

	if (__signatures.count(expression.op) || expression.op == Operator::MAP) {
		for (CodeScope* scope: expression.blocks) {
			AbstractPass::process(scope, context);
		}

		return boost::none;
	}

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

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

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

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