/* 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:   cfatemporalseqpass.cpp
 * Author: pgess
 *
 * Created on February 4, 2019, 4:43 PM
 */

/**
 *  \file cfatemporalseqpass.h
 *  \brief Context Blocks' compilation order analysis. 
 */

#include "pass/cfatemporalseqpass.h"
#include "analysis/temporalseqgraph.h"

using namespace xreate::cfa;
using namespace std; 

namespace xreate {
  template<>
  cfa::Socket defaultValue<cfa::Socket>() {
    const cfa::TemporalNode invalidNode{(ScopePacked) - 1, cfa::TemporalOperator::EMPTY};
    return make_pair(invalidNode, invalidNode);
  };
}

CFATemporalSeqPass::CFATemporalSeqPass(PassManager* manager)
: AbstractPass(manager), __graph(new TemporalSeqGraph(manager->transcend)) { }

void
CFATemporalSeqPass::finish() {
  man->transcend->registerReport(__graph);
  return AbstractPass::finish();
}

void
CFATemporalSeqPass::process(
  const Expression &expression,
  PassContext context,
  const std::string &varDecl) {

  TranscendLayer* transcend = man->transcend;

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

    switch(op) {
    case Operator::QUERY_LATE:
    case Operator::MAP:
    case Operator::FOLD:
    case Operator::INF:
    {
      __graph->addSubScopes(context.scope,
        expression.blocks);
      break;
    }

    case Operator::IF:
    case Operator::SWITCH:
    case Operator::SWITCH_LATE:
    case Operator::SWITCH_VARIANT:
    {
      __graph->addBranchScopes(context.scope,
        expression.blocks);
      break;
    }

    case Operator::SEQUENCE:
    {
      auto socket = __graph->addSocket(context.scope, TemporalOperator::AND);
      auto nodeFrom = socket.first;

      for(auto scopeIt = expression.blocks.begin(); scopeIt != expression.blocks.end(); ++scopeIt) {
        ScopePacked scope = transcend->pack(*scopeIt);
        TemporalNode node{scope, TemporalOperator::SCOPE};

        __graph->connect(nodeFrom, node);
        nodeFrom = node;
      }

      __graph->connect(nodeFrom, socket.second);
    }

    default:
      break;
    }
  }

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

void
CFATemporalSeqPass::processFnCall(ManagedFnPtr functionCallee, PassContext context) {
  auto callerSock = __graph->addSocket(context.scope, TemporalOperator::AND);
  auto calleeSock = __graph->getFnSocket(functionCallee);

  __graph->connectGuarded(callerSock, calleeSock);
}

void
CFATemporalSeqPass::processFnCallUncertain(const std::string& calleeName, const std::list<ManagedFnPtr>& candidates, PassContext context) {
  auto callerSock = __graph->addSocket(context.scope, TemporalOperator::AND);
  auto calleeSock = __graph->getUncertainFnSocket(calleeName, candidates);

  __graph->connectGuarded(callerSock, calleeSock);
}

void
CFATemporalSeqPass::process(ManagedFnPtr function) {
  TranscendLayer* transcend = man->transcend;

  Socket fnSocket = __graph->getFnSocket(function);
  TemporalNode entryNode{
    transcend->pack(function->getEntryScope()),
    TemporalOperator::SCOPE
  };

  __graph->connect(fnSocket.first, entryNode);
  __graph->connect(entryNode, fnSocket.second);

  return Parent::process(function);
}