#include "dfgpass.h"
#include "passmanager.h"
#include "clasplayer.h"
#include <boost/format.hpp>
using namespace std;

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

SymbolNode
DFGPass::process(ManagedFnPtr function){
	SymbolNode symbolRet = AbstractPass::process(function);

    if (SymbolPacked* symbolRetPacked = boost::get<SymbolPacked>(&symbolRet)){
		Expression retExpr(Operator::CALL, {Atom<Identifier_t>(Config::get("clasp.ret.symbol")),
            Atom<Identifier_t>(boost::str(boost::format("(%1%, %2%)")
                                                %(symbolRetPacked->identifier)
                                                % (symbolRetPacked->scope)))
		});

		const std::vector<Tag> tag{{retExpr, TagModifier::NONE}};
		clasp->cfagraph.addFunctionNodeTags(function->getName(), tag);

    }	else if (SymbolTransient* symbT = boost::get<SymbolTransient>(&symbolRet)){
		std::vector<Tag> tags;
		tags.reserve(symbT->tags.size());

		const std::string stmntRetTag = Config::get("clasp.ret.tag");
		std::transform(symbT->tags.begin(), symbT->tags.end(), std::inserter(tags, tags.begin()),
				[&stmntRetTag](const Expression& e) {
                    Expression tag(Operator::CALL, {Atom<Identifier_t>(string(stmntRetTag)), e});
                    return Tag{e, TagModifier::NONE};
		});

		clasp->cfagraph.addFunctionNodeTags(function->getName(), tags);
	}

    return SymbolInvalid();
}

SymbolNode
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) {
            SymbolNode nodeThis = clasp->pack(context.scope->findSymbol(decl), context.function->getName() + ":" + decl);
            __context.graph.addTag(nodeThis, Expression(tag.second));
        }
    }

    switch(expression.__state) {
        case Expression::IDENT: {
            const string& ident = expression.getValueString();
        	SymbolNode nodeFrom = AbstractPass::process(expression, context, decl);
            SymbolPacked nodeTo = clasp->pack(context.scope->findSymbol(ident), context.function->getName() + ":" + ident);

            __context.graph.addLink(nodeTo, nodeFrom, DFGConnection::ALIAS);
            return nodeTo;
        }

        default: break;
    }

    //special case for NONE value:
    if (expression.isNone()){
        return SymbolTransient{{Atom<Identifier_t>(Config::get("clasp.nonevalue"))}};
    }


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

            std::vector<SymbolNode> 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 SymbolInvalid();

            	// set calling relations:
            CodeScope *scopeRemote = function->getEntryScope();
            std::vector<SymbolNode>::iterator nodeActual = operands.begin();
            for (const std::string &identFormal: scopeRemote->__args) {
                    const Symbol &symbolFormal = scopeRemote->findSymbol(identFormal);
                    __context.graph.addLink(clasp->pack(symbolFormal, name + ":" + identFormal), *nodeActual, DFGConnection::OPT);
                ++nodeActual;
            }

            	//TODO need SymbolTransient::connection mark in order to represent OPT connection
            return clasp->pack(Symbol{0, scopeRemote}, name + ": *retv");
        }

        default: break;
    }



    SymbolNode result = AbstractPass::process(expression, context, decl);

    if (expression.__state != Expression::COMPOUND){
        return result;
	}

    std::vector<SymbolNode> operands;
	if (__signatures.count(expression.op)) {
		const Expression &scheme = __signatures.at(expression.op);

		operands.reserve(expression.getOperands().size());

        if (expression.op == Operator::CALL || expression.op == Operator::INDEX){
            string caption = expression.getValueString();
            operands.push_back(process(Expression(move(caption)), context, ""));
        }

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

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

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

			++arg; ++tag;
		}

		Expression retTag = *scheme.getOperands().begin();
		if (retTag.__state != Expression::INVALID) {
            __context.graph.addTag(result, move(retTag));
		}
	}

    // adhoc for MAP case, TODO reorganize code in more clear manner
    if (expression.op == Operator::MAP) {
        SymbolNode 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.function->getName() + ":" + decl);
        SymbolPacked* nodeFromPacked = boost::get<SymbolPacked>(&nodeFrom);
        assert(nodeFromPacked);
        __context.graph.addLink(move(nodeTo), *nodeFromPacked, DFGConnection::PROTO);
    }

    return result;
}

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));
}

template<>
SymbolNode defaultValue(){return SymbolInvalid();};

}
