/* 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/.
 *
 * File:   DFAGraph.h
 * Author: pgess <v.melnychenko@xreate.org>
 *
 */

/**
 *  \file dfagraph.h
 *  \brief Data Flow Analysis(DFA) graph representation
 *
 */

#include "analysis/dfagraph.h"
#include "analysis/utils.h"

using namespace std;
using namespace xreate::analysis;

namespace xreate {  namespace dfa {

void
DFACallInstance::print(std::ostringstream& output) const{
    boost::format formatArgs;
    boost::format formatInstance("dfa_callfn(%1%, %2%).");

    switch (type) {
        case WEAK:
            formatArgs = boost::format("weak(dfa_callargs(%1%, %2%, %3%)).");
            break;

        case STRONG:
            formatArgs = boost::format("weak(dfa_callargs(%1%, %2%, %3%)).\ndfa_callargs(%1%, %2%, %3%).");
            break;
    }

    output << formatInstance
        % analysis::writeSymbolNode(retActual)
        % fnName
        << endl;

    for(std::pair<SymbolPacked, SymbolNode> rec: args) {
        SymbolNode argFormal(rec.first);

        output << formatArgs
            % analysis::writeSymbolNode(retActual)
            % analysis::writeSymbolNode(argFormal)
            % analysis::writeSymbolNode(rec.second)
            << endl;
    }
}

void
DFAGraph::addDependency(const SymbolNode& node, const SymbolNode& subnode){
    __dependencies.emplace(node, subnode);

    if (boost::get<SymbolPacked>(&node)){
        __usedSymbols.insert(node);
    }

    if (boost::get<SymbolPacked>(&subnode)){
        __usedSymbols.insert(node);
    }
}

void
DFAGraph::printDependencies(std::ostringstream& output) const{
    for(const SymbolNode& root: __roots){
        printDependency(output, root, root);
    }
}

void
DFAGraph::printDependency(std::ostringstream& output, const SymbolNode& nodeCurrent, const SymbolNode& nodeDependent) const {
    auto range = __dependencies.equal_range(nodeCurrent);
    for (auto it = range.first; it != range.second; ++it){
        if (boost::get<SymbolAnonymous>(&it->second)){
            if (!__usedSymbols.count(it->second)){
                printDependency(output, it->second, nodeDependent);
                continue;
            }
        }

        boost::format formatDependency("dfa_depends(%1%, %2%).");

        output << formatDependency
            % analysis::writeSymbolNode(nodeDependent)
            % analysis::writeSymbolNode(it->second)
            << endl;

        printDependency(output, it->second, it->second);
    }
}

void
DFAGraph::printInplaceAnnotation(const SymbolNode& node, const Expression& expression) {
    // write down in-place expression tags:
    boost::format formatBind("bind(%1%, %2%).");
    __usedSymbols.insert(node);

    for (const string& tag: xreate::analysis::compile(expression)) {
        __output << formatBind
            % analysis::writeSymbolNode(node)
            % tag
            << endl;
    }
}

void
DFAGraph::printLateAnnotation(const SymbolNode& node,
                            const Expression& expression,
                            const std::list<latereasoning::LateParameter>& symbols,
                            const std::list<std::string>& domains){
    boost::format formatLateAnnotation("late(%1%, (%2%), (%3%), %4%):- %5%.");
    boost::format formatDom("%1%(%2%)");
    std::list<std::string> exprSerialized = xreate::analysis::compile(expression);
    assert(exprSerialized.size() == 1);

    list<string> identSymbols, identNames, domainsSerialised;

    auto domainI = domains.begin();
    for(auto symbol: symbols){
        identSymbols.push_back(analysis::writeSymbolNode(symbol.second).str());
        identNames.push_back(symbol.first);
        domainsSerialised.push_back((formatDom % *domainI % symbol.first).str());
        ++domainI;
    }

    __output << formatLateAnnotation
        % analysis::writeSymbolNode(node)
        % boost::algorithm::join(identSymbols, ", ")
        % boost::algorithm::join(identNames, ", ")
        % exprSerialized.front()
        % boost::algorithm::join(domainsSerialised, "; ")
        << endl;
}

void
DFAGraph::printAlias(const SymbolNode& symbFormal, const SymbolNode& symbActual){
    __usedSymbols.insert(symbFormal); __usedSymbols.insert(symbActual);

    boost::format formatAlias("dfa_alias(%1%, %2%).");
    __output << formatAlias
        % analysis::writeSymbolNode(symbFormal)
        % analysis::writeSymbolNode(symbActual)
        << endl;
}

void
DFAGraph::printWeakAlias(const SymbolNode& symbFormal, const SymbolNode& symbActual){
    __usedSymbols.insert(symbFormal); __usedSymbols.insert(symbActual);

    boost::format formatAlias("weak(dfa_alias(%1%, %2%)).");
    __output << formatAlias
        % analysis::writeSymbolNode(symbFormal)
        % analysis::writeSymbolNode(symbActual)
        << endl;
}

void
DFAGraph::printFunctionRet(ManagedFnPtr function, const SymbolNode& symbolRet){
    boost::format formatRet("dfa_fnret(%1%, %2%).");
    __usedSymbols.insert(symbolRet);

    __output << formatRet
        % function->getName()
        % analysis::writeSymbolNode(symbolRet)
        << endl;

    __roots.insert(symbolRet);
}

void
DFAGraph::addCallInstance(DFACallInstance&& instance){
    __usedSymbols.insert(instance.retActual);
    for(const auto arg: instance.args){
        __usedSymbols.insert(SymbolNode(arg.first));
        __usedSymbols.insert(arg.second);
    }

    __callInstances.push_back(std::move(instance));
}

void
DFAGraph::print(std::ostringstream& output) const{
    output << endl << "%\t\tStatic analysis: DFA" << endl;

    //Dependencies
    printDependencies(output);

    //Add generated report
    output << __output.str() << endl;

    //Call instances
    for(const DFACallInstance& instance: __callInstances){
        instance.print(output);
    }

    output << endl;
}

void
DFAGraph::printSymbols(TranscendLayer* transcend){
    boost::format formatHint("shint(%1%, \"%2%\").");

    for (const SymbolNode& node : __usedSymbols) {
        __output << "v(" << analysis::writeSymbolNode(node) << "). ";

        if (const SymbolPacked* symbol = boost::get<SymbolPacked>(&node)){
            __output << formatHint % analysis::writeSymbolNode(node) % transcend->getHintForPackedSymbol(*symbol);
        }
        __output << endl;
    }
}

void
DFAGraph::printOperator(Operator op, std::list<SymbolNode>&& operands, int dataOpListSize){
    std::string opStr;
    switch(op){
        case Operator::MAP:         opStr = "map"; break;
        case Operator::FOLD:        opStr = "fold"; break;
        case Operator::LIST:        opStr = "list"; break;
        case Operator::LIST_RANGE:  opStr = "list_range"; break;
        case Operator::INDEX:       opStr = "index"; break;
        default: assert(false);
    }

    std::ostringstream bufOperands;
    for(const SymbolNode& operand: operands){
        __usedSymbols.insert(operand);
        bufOperands << analysis::writeSymbolNode(operand) << ", ";
    }

    if(op == Operator::LIST){
        bufOperands << dataOpListSize << ", ";
    }

    boost::format formatOperator("ast_op_%1%(%2%).");
    __output << (formatOperator % opStr % bufOperands.str().substr(0, bufOperands.str().size() - 2)) << endl;
}

}} //end of namespace xreate::dfa
