#ifndef PLS_TASK_H
#define PLS_TASK_H

#include "pls/internal/scheduling/cont.h"
#include "pls/internal/scheduling/memory_block.h"

#include "pls/internal/helpers/profiler.h"

namespace pls {
namespace internal {
namespace scheduling {

/**
 * A task to be executed by the runtime system.
 * Tasks are guaranteed to be executed exactly once.
 *
 * Override the execute_internal() method for your custom code.
 */
class base_task {
 public:
  /**
   * Executes the task and stores its result in the correct continuation.
   * The caller must handle finishing the continuation/informing it that task two was finished.
   */
  void execute() {
    execute_internal();
  }

  base_cont *get_cont() {
    return cont_;
  }

 protected:
  explicit base_task(base_cont *cont) : cont_{cont} {};

  /**
   * Overwrite this with the actual behaviour of concrete tasks.
   */
  virtual void execute_internal() = 0;

  base_cont *cont_;
};

template<typename F>
class task : public base_task {
 public:
  template<typename FARG>
  explicit task(FARG &&function, base_cont *cont)
      : base_task{cont}, function_{std::forward<FARG>(function)} {}

  void execute_internal() override {
    PROFILE_TASK("execute_internal")
    auto result = function_();
    if (result.fast_path()) {
      cont_->store_right_result(std::move(result));
    }
  }

 private:
  F function_;

};

}
}
}

#endif //PLS_TASK_H
