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

/**
 * \file    cfapass.h
 * \brief   Control Flow Analysis(CFA)
 */

#include "cfapass.h"
#include "analysis/cfagraph.h"
#include "analysis/DominatorsTreeAnalysisProvider.h"

#include <boost/range/iterator_range_core.hpp>

using namespace std;
using namespace xreate::cfa;

void
CFAPassBasic::initSignatures(){
    auto range = man->root->__interfacesData.equal_range(CFA);
    for (auto i = range.first; i!= range.second; ++i){
            __signatures.emplace(i->second.op, i->second);
    }
}

void
CFAPassBasic::run(){
    initSignatures();

    return AbstractPass::run();
}

void
CFAPassBasic::finish(){
    man->clasp->registerReport(__context.graph);

    dominators::DominatorsTreeAnalysisProvider* reportDominators = new dominators::DominatorsTreeAnalysisProvider();
    reportDominators->run(__context.graph);
    man->clasp->registerReport(reportDominators);

    return AbstractPass::finish();
}

void
CFAPassBasic::processFnCall(ManagedFnPtr function, PassContext context){
    ClaspLayer* clasp = man->clasp;
    __context.graph->addCallConnection(clasp->pack(context.scope), function->getName());

    return AbstractPass::processFnCall(function, context);
}

void
CFAPassBasic::processFnCallUncertain(ManagedFnPtr function, PassContext context){
    ClaspLayer* clasp = man->clasp;
    __context.graph->addCallConnection(clasp->pack(context.scope), function->getName());

    return AbstractPass::processFnCallUncertain(function, context);
}

void
CFAPassBasic::process(CodeScope* scope, PassContext context, const std::string& hintBlockDecl){
    ClaspLayer* clasp = man->clasp;

    const CodeScope* scopeParent = context.scope;
    ScopePacked scopeId = clasp->pack(scope);

    //Parent Relations
    if (scopeParent){
        __context.graph->addParentConnection(scopeId, clasp->pack(scopeParent));
    } else {
        __context.graph->addParentConnection(scopeId, context.function->getName());
    }

    //TOTEST scope annotations
    //SECTIONTAG context gather scope annotations
    __context.graph->addScopeAnnotations(scopeId, scope->tags);

    __context.graph->addContextRules(scopeId, scope->contextRules);

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

//TOTEST scope annotations via scheme
void
CFAPassBasic::process(const Expression& expression, PassContext context, const std::string& varDecl){
    ClaspLayer* clasp = man->clasp;

    if (expression.__state == Expression::COMPOUND){
        Operator  op= expression.op;

        if (__signatures.count(op)) {
            assert(expression.blocks.size());

            for (const auto& scheme: boost::make_iterator_range(__signatures.equal_range(expression.op))) {
                __context.graph->addScopeAnnotations(clasp->pack(expression.blocks.front()), scheme.second.getOperands());
            }
        }
    }

    return AbstractPass::process(expression, context, varDecl);
}

void
CFAPassBasic::process(ManagedFnPtr function){
    __context.graph->addFunctionAnnotations(function->getName(), function->getTags());
    return AbstractPass::process(function);
}

CFAPassBasic::CFAPassBasic(PassManager* manager)
: AbstractPass(manager)
      , __context{new CFAGraph(manager->clasp)}
{}

/****************************SCOPE DEPENDENCIES********************************/
void
CFAPassDependenciesDecorator::process(const Expression& expression, PassContext context, const std::string& varDecl){
    ClaspLayer* clasp = man->clasp;

    if (expression.__state == Expression::COMPOUND)
    switch(expression.op){
        case Operator::SEQUENCE:{
            ScopePacked scopePrev = clasp->pack(expression.blocks.front());
            for(auto scopeIt= ++expression.blocks.begin(); scopeIt != expression.blocks.end(); ++scopeIt){
                ScopePacked scopeCurrent = clasp->pack(*scopeIt);
                __context.graph->addDependency(scopeCurrent, scopePrev);
                scopePrev = scopeCurrent;
            }
            break;
        }

        default: break;
    }

    return Parent::process(expression, context, varDecl);
}

void
CFAPassDependenciesDecorator::processFnCall(ManagedFnPtr function, PassContext context){
    ClaspLayer* clasp = man->clasp;

    const CodeScope* scopeCaller = context.scope;
    assert(scopeCaller);
    ScopePacked scopeCallerPacked = clasp->pack(scopeCaller);

    if(__context.graph->isDependent(scopeCallerPacked)){
        ScopePacked scopeCalleePacked = clasp->pack(function->getEntryScope());
        __context.graph->transmitDependencies(scopeCalleePacked, scopeCallerPacked);
    }

    Parent::processFnCall(function, context);
}

void
CFAPassDependenciesDecorator::processFnCallUncertain(ManagedFnPtr function, PassContext context){
    ClaspLayer* clasp = man->clasp;

    const CodeScope* scopeCaller = context.scope;
    assert(scopeCaller);
    ScopePacked scopeCallerPacked = clasp->pack(scopeCaller);

    if(__context.graph->isDependent(scopeCallerPacked)){
        ScopePacked scopeCalleePacked = clasp->pack(function->getEntryScope());
        __context.graph->transmitDependencies(scopeCalleePacked, scopeCallerPacked);
    }

    Parent::processFnCallUncertain(function, context);
}

void
CFAPassDependenciesDecorator::process(CodeScope* scope,
        PassContext context, const std::string& hintBlockDecl){

    ClaspLayer* clasp = man->clasp;
    const CodeScope* scopeParent = context.scope;

    if (scopeParent){
        ScopePacked scopePacked = clasp->pack(scope);
        ScopePacked scopeParentPacked = clasp->pack(scopeParent);
        if (!__context.graph->isDependent(scopePacked) &&
            __context.graph->isDependent(scopeParentPacked)) {
            __context.graph->transmitDependencies(scopePacked, scopeParentPacked);
        }
    }

    Parent::process(scope, context, hintBlockDecl);
}
/**
 * \class xreate::cfa::CFAPass
 * \details Provides CFA, important analysis for reasoning. Iterates over AST and stores collected data in CFAGraph
 */