/* 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/.
 *
 * Author: pgess <v.melnychenko@xreate.org>
 *
 * dfapass.cpp
 */

/**
 * \file    dfapass.h
 * \brief   Data Flow Analysis(DFA)
 */

//DEBT DFA represent VersionaPass in declarative form using applyDependencies
//    applyDependencies(expression, context, cache, decl);

//DEBT DFA prepare static annotations and represent InterpretationPass in declarative form
//    applyStaticAnnotations(expression, context, cache, decl);

//DEBT DFA Eliminate dfa schemes

#include "pass/dfapass.h"
#include "xreatemanager.h"
#include "clasplayer.h"

#include <boost/format.hpp>
#include <boost/variant/variant.hpp>

using namespace std;
namespace xreate { namespace dfa {

DFAPass::DFAPass(PassManager* manager)
    : AbstractPass(manager)
    , graph{new DFAGraph()}
    , clasp(manager->clasp)
{}

void
DFAPass::processCallInstance(const Expression& expr, PassContext context, const SymbolNode& result){
    const string &nameCalleeFunction = expr.getValueString();

    //TODO implement processFnCall/Uncertain
    list<ManagedFnPtr> variantsCalleeFunction = man->root->getFunctionSpecializations(nameCalleeFunction);

    vector<SymbolNode> operands;
    operands.reserve(expr.getOperands().size());

    for (const Expression& arg: expr.getOperands()){
        operands.push_back(process(arg, context));
    }
    graph->markSymbolsAsUsed(operands);
    graph->markSymbolsAsUsed(std::vector<SymbolNode>{result});

    //Set calling relations:
    for (ManagedFnPtr function: variantsCalleeFunction){
        CodeScope *scopeRemote = function->getEntryScope();
        DFACallInstance callInstance;
        callInstance.id = expr.id;
        callInstance.fnName = function->getName();

        std::vector<SymbolNode>::const_iterator nodeActual = operands.begin();
        for (const std::string &identFormal: scopeRemote->__bindings){
            const ScopedSymbol symbolFormal{scopeRemote->__identifiers.at(identFormal), versions::VERSION_NONE};
            SymbolPacked symbolFormalPacked = clasp->pack(Symbol{symbolFormal, scopeRemote});
            callInstance.args.push_back(std::make_pair(symbolFormalPacked, *nodeActual));

            graph->addDependency(symbolFormalPacked, *nodeActual);
            ++nodeActual;
        }

        callInstance.retActual = result;
        SymbolNode retFormal = SymbolNode(clasp->pack(Symbol{ScopedSymbol::RetSymbol, scopeRemote}));
        graph->addDependency(result, retFormal);

        graph->addCallInstance(std::move(callInstance));
    }


}

void
DFAPass::processDependencies(const SymbolNode& node, const Expression& expression, PassContext context, ProcessingCache& cache){
    cache.operands.reserve(expression.getOperands().size());
    for (const Expression &op: expression.getOperands()) {
        const SymbolNode& subnodeOperand = process(op, context);

        cache.operands.push_back(subnodeOperand);
        graph->addDependency(node, subnodeOperand);
    }

    cache.blocks.reserve(expression.blocks.size());
    for (CodeScope* block : expression.blocks) {
        const SymbolNode& subnodeBlock = process(block, context);

        cache.blocks.push_back(subnodeBlock);
        graph->addDependency(node, subnodeBlock);
    }
}

SymbolNode
DFAPass::process(const Expression& expression, PassContext context, const std::string& varDecl)
{
    SymbolNode result;

    if (Attachments::exists<SymbolAlias>(expression))
    {
        Symbol varSymbol = Attachments::get<SymbolAlias>(expression);
        result = clasp->pack(varSymbol, varDecl);

    } else if (expression.__state == Expression::IDENT && expression.tags.size() == 0) {
        Symbol varSymbol = Attachments::get<IdentifierSymbol>(expression);
        result = clasp->pack(varSymbol, expression.getValueString());

    } else {
        result = SymbolAnonymous{expression.id};
    }

    graph->printInplaceAnnotations(result, expression);

    switch(expression.__state){
        case Expression::COMPOUND:    {
            switch (expression.op) {
                case Operator::CALL:
                    processCallInstance(expression, context, result);
                    break;

                default:
                    ProcessingCache cache;
                    processDependencies(result, expression, context, cache);
                    break;
            }
            break;
        }

        case Expression::IDENT: {
            SymbolNode symbIdent = AbstractPass<SymbolNode>::process(expression, context, varDecl);

            if (!(result == symbIdent)) {
                graph->addDependency(result, symbIdent);
                graph->printAlias(result, symbIdent);
            }

            break;
        }

        case Expression::NUMBER:
        case Expression::STRING: {
            break;
        }

        case Expression::INVALID:
        case Expression::BINDING: {
            assert(false);
            break;
        }
    }

    return result;
}

SymbolNode
DFAPass::process(CodeScope* scope, PassContext context, const std::string& hintBlockDecl){
    for(const std::string& binding: scope->__bindings){
        Symbol bindingSymbol{scope->getSymbol(binding), scope};
        SymbolPacked bindingPacked = clasp->pack(bindingSymbol, binding);

        getSymbolCache().setCachedValue(bindingSymbol, SymbolNode(bindingPacked));
    }

    return AbstractPass<SymbolNode>::process(scope, context, hintBlockDecl);
}

SymbolNode
DFAPass::process(ManagedFnPtr function){
    SymbolNode result = AbstractPass<SymbolNode>::process(function);

    graph->printFunctionRet(function, result);
    return result;
}

void
DFAPass::finish(){
    clasp->registerReport(graph);

    //Declare symbols:
    graph->printSymbols(clasp);

    AbstractPass::finish();
}

} //end of namespace dfa

template<>
dfa::SymbolNode defaultValue(){
    assert(false);
}

} //xreate namespace

/**
 * \class xreate::dfa::DFAPass
 * \details Provides DFA, important analysis for reasoning. Iterates over AST and stores collected data in DFAGraph
 */
