#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(CodeScope* scope, PassContext context, const std::string& hintBlockDecl){
	/*
	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.addFunctionAnnotations(function->getName(), tags);
	}
	*/

	const SymbolNode& retActual = AbstractPass::process(scope, context, hintBlockDecl);
	const SymbolPacked& retFormal{0, clasp->pack(scope)};
	__context.graph.addConnection(retFormal, retActual, DFGConnection::ALIAS);

	return retFormal;
}

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.addAnnotation(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.addConnection(nodeTo, nodeFrom, DFGConnection::ALIAS);
            return nodeTo;
        }

        default: break;
    }

//TODO Null ad hoc DFG implementation
//    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));
            }

            	//TODO implement processFnCall/Uncertain
            list<ManagedFnPtr> functions = man->root->getFunctionVariants(name);
            if (functions.size()!=1) return SymbolInvalid();
            ManagedFnPtr function= functions.front();

            	// 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.addConnection(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.addAnnotation(*arg, Expression(*tag));
			}

			++arg; ++tag;
		}

		Expression retTag = *scheme.getOperands().begin();
		if (retTag.__state != Expression::INVALID) {
            __context.graph.addAnnotation(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.addConnection(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();};

}
