#ifndef SPEC_CONTROL_HH
#define SPEC_CONTROL_HH

#include <flecsi/flog.hh>
#include <flecsi/run/control.hh>

namespace spec::control {

/*----------------------------------------------------------------------------*
  Base control policy type used to define control data and interface.
 *----------------------------------------------------------------------------*/
namespace detail {
template<template<std::size_t> typename S, std::size_t D>
struct control_base {

  /// Control Model Base Constructor
  /// @param steps Maximum number of time steps.
  /// @param log   Logging frequency.
  control_base(std::size_t steps, std::size_t log) : steps_(steps), log_(log) {}

  S<D> & state() {
    return state_;
  }
  std::size_t step() const {
    return step_;
  }
  std::size_t steps() const {
    return steps_;
  }
  std::size_t log() const {
    return log_;
  }

protected:
  std::size_t step_{0};
  std::size_t steps_;
  std::size_t log_;
  S<D> state_;
};
} // namespace detail

/*----------------------------------------------------------------------------*
  Basis control model: initialize -> advance -> analyze -> finalize.
 *----------------------------------------------------------------------------*/
namespace basic {
/// Control Points.
enum class cp {
  /// Application initialization.
  initialize,
  /// Time evolution (cycled).
  advance,
  /// Running analysis (cycled).
  analyze,
  /// Application finalization.
  finalize
};

inline const char *
operator*(cp control_point) {
  switch(control_point) {
    case cp::initialize:
      return "initialize";
    case cp::advance:
      return "advance";
    case cp::analyze:
      return "analyze";
    case cp::finalize:
      return "finalize";
  }
  flog_fatal("invalid control point");
}

template<template<std::size_t> typename S, std::size_t D>
struct control_policy : detail::control_base<S, D>, flecsi::run::control_base {

  using control_points_enum = cp;

  /// Control Model Constructor
  /// @param steps Maximum number of time steps.
  /// @param log   Logging frequency.
  control_policy(std::size_t steps, std::size_t log)
    : detail::control_base<S, D>(steps, log) {}

  static bool cycle_control(control_policy & cp) {
    return cp.step_++ < cp.steps_ && S<D>::cycle_control(cp);
  }

  using evolve = cycle<cycle_control, point<cp::advance>, point<cp::analyze>>;

  using control_points =
    list<point<cp::initialize>, evolve, point<cp::finalize>>;
};
} // namespace basic

} // namespace spec::control

#endif // SPEC_CONTROL_HH
