//===-- app/AppSensor.cpp ---------------------------------------*- C++ -*-===//
//
//                                 The RoSA Framework
//
// Distributed under the terms and conditions of the Boost Software License 1.0.
// See accompanying file LICENSE.
//
// If you did not receive a copy of the license file, see
// http://www.boost.org/LICENSE_1_0.txt.
//
//===----------------------------------------------------------------------===//
///
/// \file app/AppSensor.cpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017-2020
///
/// \brief Implementation of rosa/app/AppSensor.hpp.
///
//===----------------------------------------------------------------------===//

#include "rosa/app/AppSensor.hpp"

#include "rosa/app/AppSystem.hpp"

namespace rosa {
namespace app {

bool AppSensor::inv(void) const noexcept {
  // Check execution policy.
  // \note The \c rosa::System the \c rosa::Unit is created with is a
  // \c rosa::AppSystem.
  const AppSystem &DS = static_cast<AppSystem &>(Unit::system());
  if (!ExecutionPolicy || !ExecutionPolicy->canHandle(Self, DS)) {
    return false;
  }

  // Check the index of next expected element from the *master*.
  const token_size_t MITL = lengthOfToken(MasterInputType);
  if ((MITL != 0 && MasterInputNextPos >= MITL) ||
      (MITL == 0 && MasterInputNextPos != 0)) {
    return false;
  }

  // All checks were successful, the invariant is held.
  return true;
}

id_t AppSensor::masterId(void) const noexcept {
  ASSERT(Master);
  return unwrapAgent(*Master).Id;
}

AppSensor::~AppSensor(void) noexcept {
  ASSERT(inv());
  LOG_TRACE_STREAM << "Destroying AppSensor " << FullName << "..." << std::endl;

  // Make sure \p this object is not a registered *slave*.
  if (Master) {
    ASSERT(unwrapAgent(*Master).Kind == atoms::AgentKind); // Sanity check.
    AppAgent &M = static_cast<AppAgent &>(unwrapAgent(*Master));
    ASSERT(M.positionOfSlave(self()) != M.NumberOfInputs); // Sanity check.
    M.registerSlave(M.positionOfSlave(self()), {});
    Master = {};
  }
}

const AppExecutionPolicy &AppSensor::executionPolicy(void) const noexcept {
  ASSERT(inv());
  return *ExecutionPolicy;
}

bool AppSensor::setExecutionPolicy(
    std::unique_ptr<AppExecutionPolicy> &&EP) noexcept {
  ASSERT(inv());
  LOG_TRACE_STREAM << "AppSensor " << FullName << " setting execution policy "
                   << *EP << std::endl;
  bool Success = false;
  // \note The \c rosa::System the \c rosa::Unit is created with is a
  // \c rosa::AppSystem.
  const AppSystem &DS = static_cast<AppSystem &>(Unit::system());
  if (EP && EP->canHandle(self(), DS)) {
    ExecutionPolicy.swap(EP);
    Success = true;
  } else {
    LOG_TRACE_STREAM << "Execution policy " << *EP
                     << " cannot handle AppSensor " << FullName << std::endl;
  }
  ASSERT(inv());
  return Success;
}

Optional<AgentHandle> AppSensor::master(void) const noexcept {
  ASSERT(inv());
  return Master;
}

void AppSensor::registerMaster(const Optional<AgentHandle> _Master) noexcept {
  ASSERT(inv() && (!_Master || unwrapAgent(*_Master).Kind == atoms::AgentKind));

  Master = _Master;

  ASSERT(inv());
}

void AppSensor::clearSimulationDataSource(void) noexcept {
  ASSERT(inv());
  SFP = nullptr;
  ASSERT(inv());
}

bool AppSensor::simulationDataSourceIsSet(void) const noexcept {
  ASSERT(inv());
  return SFP != nullptr;
}

void AppSensor::handleTrigger(atoms::Trigger) noexcept {
  ASSERT(inv() && MasterInputNextPos == 0);

  // Process master-input.
  MFP();

  // Obtain the next sensory value.
  // \note Execution policy is respected within the data source function.

  // Use \c rosa::app::AppSensor::SFP if set, otherwise
  // \c rosa::app::AppSensor::FP.
  const H &F = SFP ? SFP : FP;
  F();

  ASSERT(inv());
}

} // End namespace app
} // End namespace rosa
