/* 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:   CFAGraph.cpp
 * Author: pgess <v.melnychenko@xreate.org>
 *
 * Created on June 27, 2016, 2:09 PM
 */

/**
 *  \file  cfagraph.h
 *  \brief Control Flow Analysis(CFA) graph representation
 */

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

using namespace xreate::cfa;
using namespace std;

void
CFAGraph::print(std::ostringstream& output) const {
  const std::string& atomBinding = Config::get("transcend.bindings.function");
  const std::string& atomBindingScope = Config::get("transcend.bindings.scope");

  output << endl << "%\t\tStatic analysis: CFA" << endl;
  output << __outputPrecomputed.str();

  //show function tags
  int counterTags = 0;
  std::ostringstream bufFunctionNames;
  boost::format formatFunction("function(%1%).");
  boost::format formatBind(atomBinding + "(%1%, %2%).");
  for (auto function : this->__fnNodes.left) {
    const  auto tags = this->__fnTags.equal_range(function.first);
    if (tags.first == tags.second) {
      //no tags
      bufFunctionNames << "; " << function.second ;
      continue;
    }

    output << formatFunction % (function.second) << std::endl;
    for (const auto& tag_ : boost::make_iterator_range(tags)) {
      const Expression& tag = tag_.second;

      list<string> tagRaw = xreate::analysis::compile(tag);
      assert(tagRaw.size() == 1);

      output << formatBind
        % (function.second)
        % (tagRaw.front())
        << endl;
      ++counterTags;
    }
  }

  if (bufFunctionNames.tellp()) {
    output << formatFunction % (bufFunctionNames.str().substr(2)) << std::endl;
  }

  if (counterTags == 0) {
    output << "%no function tags at all" << endl;
  }

  //declare scopes
  boost::format formatScope("scope(0..%1%).");
  output << formatScope % (__transcend->getScopesCount() - 1) << std::endl;

  //show context rules:
  for (auto rule : this->__contextRules) {
    output << ContextRule(rule.second).compile(rule.first) << std::endl;
  };

  //show scope tags:
  counterTags = 0;
  boost::format formatScopeBind(atomBindingScope + "(%1%, %2%, strong).");
  for (auto entry : this->__scopeTags) {
    ScopePacked scopeId = entry.first;
    const Expression& tag = entry.second;
    list<string> tagRaw = xreate::analysis::compile(tag);
    assert(tagRaw.size() == 1);

    output << formatScopeBind % scopeId % (tagRaw.front()) << endl;
    ++counterTags;
  }

  if (counterTags == 0) {
    output << "%scope tags: no tags at all" << endl;
  }

  //parent connections
  //TOTEST CFG parent function
  boost::format formatFunctionParent("cfa_parent(%1%, function(%2%)).");
  for (const auto &relation : this->__parentFnRelation) {
    const string& function = this->__fnNodes.left.at(relation.right);

    output << formatFunctionParent % relation.left % function << endl;
  }

  //TOTEST CFG parent scope
  boost::format formatScopeParent("cfa_parent(%1%, scope(%2%)).");
  for (const auto &relation : this->__parentScopeRelation) {
    output << formatScopeParent % relation.first % relation.second << endl;
  }

  //call connections
  boost::format formatCall("cfa_call(%1%, %2%).");

  for (const auto &relation : this->__callRelations) {
    const ScopePacked scopeFrom = relation.left;
    const string& functionTo = this->__fnNodes.left.at(relation.right);

    output << formatCall % (scopeFrom) % (functionTo) << endl;
  }

  //function specializations description
  boost::format formatSpecializations("cfa_function_specializations(%1%, %2%).");
  const list<ManagedFnPtr>& functions = __transcend->ast->getAllFunctions();
  for (auto f : functions) {
    if (f->guard.isValid()) {
      list<string> guardRaw = xreate::analysis::compile(f->guard);
      assert(guardRaw.size() == 1);
      output << formatSpecializations % (f->getName()) % (guardRaw.front()) << endl;
    }
  }
}

void
CFAGraph::addFunctionAnnotations(const std::string& fn, const std::map<std::string, Expression>& tags) {
  unsigned int fid = registerNodeFunction(fn);

  for (auto& tag : tags) {
    __fnTags.emplace(fid, tag.second);
  }
}

void
CFAGraph::addScopeAnnotations(const ScopePacked& scope, const std::vector<Expression>& tags) {
  for (Expression tag : tags) {
    __scopeTags.emplace(scope, tag);
  }
}

void
CFAGraph::addContextRules(const ScopePacked& scope, const std::vector<Expression>& rules) {
  for (Expression rule : rules) {
    __contextRules.emplace(scope, rule);
  }
}

void
CFAGraph::addCallConnection(const ScopePacked& callerScope, const std::string& calleeFn) {
  unsigned int idFuncTo = registerNodeFunction(calleeFn);

  __callRelations.insert(CALL_RELATIONS::value_type(callerScope, idFuncTo));
}

void
CFAGraph::addParentConnection(const ScopePacked& scopeEntry, const std::string& fnParent) {
  __parentFnRelation.insert(PARENT_FUNCTION_RELATIONS::value_type(scopeEntry, registerNodeFunction(fnParent)));
}

void
CFAGraph::addParentConnection(const ScopePacked& scopeChild, const ScopePacked& scopeParent) {
  __parentScopeRelation.emplace(scopeChild, scopeParent);
}

unsigned int
CFAGraph::registerNodeFunction(const std::string& fname) {
  auto pos = __fnNodes.left.insert(make_pair(__fnNodes.size(), fname));

  return pos.first->first;
}

void
CFAGraph::addScope(CodeScope* scope) {
  boost::format formatScopeBinding("ast_scope_binding(%1%, %2%, \"%3%\").");

  ScopePacked scopeId = __transcend->pack(scope);
  __scopesCount = max(scopeId + 1, __scopesCount);

  for (int id = 0, size = scope->__bindings.size(); id < size; ++id) {
    __outputPrecomputed <<  formatScopeBinding
      % scopeId
      % id
      % scope->__bindings.at(id)
      << endl;
  }
}

