/* 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 "pass/cfapass.h"
#include "analysis/cfagraph.h"
#include <boost/range/iterator_range_core.hpp>

using namespace std;

namespace xreate {
namespace cfa {

void
CFAPass::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
CFAPass::run() {
  initSignatures();

  return AbstractPass::run();
}

void
CFAPass::finish() {
  man->transcend->registerReport(__context.graph);

  return AbstractPass::finish();
}

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

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

void
CFAPass::processFnCallUncertain(const std::string& calleeName, const std::list<ManagedFnPtr>& candidates, PassContext context) {
  TranscendLayer* transcend = man->transcend;
  __context.graph->addCallConnection(transcend->pack(context.scope), calleeName);

  return AbstractPass::processFnCallUncertain(calleeName, candidates, context);
}

void
CFAPass::process(CodeScope* scope, PassContext context, const std::string &hintBlockDecl) {
  TranscendLayer* transcend = man->transcend;

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

  __context.graph->addScope(scope);

  //Parent Relations
  if(scopeParent) {
    __context.graph->addParentConnection(scopeId, transcend->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
CFAPass::process(const Expression &expression, PassContext context, const std::string &varDecl) {
  TranscendLayer* transcend = man->transcend;

  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(transcend->pack(expression.blocks.front()), scheme.second.getOperands());
      }
    }
  }

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

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

CFAPass::CFAPass(PassManager* manager)
: AbstractPass(manager), __context{new CFAGraph(manager->transcend)}
{
}

}
}
/**
 * \class xreate::cfa::CFAPass
 * \details Provides CFA, important analysis for reasoning. Iterates over AST and stores collected data in CFAGraph
 */
